From 8c15da99e08b9d268f4608c0d31d4d174c315a2d Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Thu, 4 Sep 2014 17:06:02 +0200 Subject: [PATCH 01/15] Initial implementation of Java scanner and parser Ported from scalac 2.11.x branch SHA 9753f23f9362b25a9f481b11dd8d51187187882a This is mostly a direct port, with few significant dotty-specific changes needed. The two more significant changes are: In dotty, the first constructor of a class is pulled out separately from the other stats in the Template. The keyword detection code (buildKeywordArray) was moved into Tokens so that it can more cleanly be shared by the Scala and Java scanners. --- src/dotty/tools/dotc/core/Flags.scala | 3 + .../tools/dotc/parsing/JavaParsers.scala | 887 ++++++++++++++++++ .../tools/dotc/parsing/JavaScanners.scala | 537 +++++++++++ src/dotty/tools/dotc/parsing/JavaTokens.scala | 92 ++ src/dotty/tools/dotc/parsing/Parsers.scala | 90 +- src/dotty/tools/dotc/parsing/Scanners.scala | 233 +++-- src/dotty/tools/dotc/parsing/Tokens.scala | 143 ++- src/dotty/tools/dotc/typer/Checking.scala | 2 +- src/dotty/tools/dotc/typer/FrontEnd.scala | 5 +- 9 files changed, 1788 insertions(+), 204 deletions(-) create mode 100644 src/dotty/tools/dotc/parsing/JavaParsers.scala create mode 100644 src/dotty/tools/dotc/parsing/JavaScanners.scala create mode 100644 src/dotty/tools/dotc/parsing/JavaTokens.scala diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index e5bf27eaeb00..6d77a324f45e 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -356,6 +356,9 @@ object Flags { /** Symbol is a Java default method */ final val DefaultMethod = termFlag(38, "") + /** Symbol is a Java enum */ + final val Enum = commonFlag(40, "") + // Flags following this one are not pickled /** Symbol always defines a fresh named type */ diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala new file mode 100644 index 000000000000..095342c48291 --- /dev/null +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -0,0 +1,887 @@ +package dotty.tools +package dotc +package parsing + +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.Flags.FlagSet + +import scala.language.implicitConversions + +import JavaTokens._ +import JavaScanners._ +import Parsers._ +import core._ +import Contexts._ +import Names._ +import NameOps._ +import Types._ +import Symbols._ +import ast.Trees._ +import Decorators._ +import StdNames._ +import dotty.tools.dotc.util.SourceFile +import util.Positions._ +import annotation.switch +import scala.collection.mutable.ListBuffer +import scala.reflect.internal.util.Collections._ + +object JavaParsers { + + import ast.untpd._ + + class JavaParser(source: SourceFile)(implicit ctx: Context) extends ParserCommon(source) { + + val definitions = ctx.definitions + import definitions._ + + val in: JavaScanner = new JavaScanner(source) + + /** The simple name of the package of the currently parsed file */ + private var thisPackageName: TypeName = tpnme.EMPTY + + /** This is the general parse entry point. + * Overridden by ScriptParser + */ + def parse(): Tree = { + val t = compilationUnit() + accept(EOF) + t + } + + // -------- error handling --------------------------------------- + + protected def skip(): Unit = { + var nparens = 0 + var nbraces = 0 + while (true) { + in.token match { + case EOF => + return + case SEMI => + if (nparens == 0 && nbraces == 0) return + case RPAREN => + nparens -= 1 + case RBRACE => + if (nbraces == 0) return + nbraces -= 1 + case LPAREN => + nparens += 1 + case LBRACE => + nbraces += 1 + case _ => + } + in.nextToken() + } + } + + def syntaxError(msg: String, skipIt: Boolean): Unit = { + syntaxError(in.offset, msg, skipIt) + } + + def syntaxError(pos: Int, msg: String, skipIt: Boolean): Unit = { + if (pos > lastErrorOffset) { + syntaxError(msg, pos) + // no more errors on this token. + lastErrorOffset = in.offset + } + if (skipIt) + skip() + } + def errorTypeTree = TypeTree().withType(ErrorType) withPos Position(in.offset) + + // --------- tree building ----------------------------- + + def rootId(name: Name) = Select(Ident(nme.ROOTPKG), name) + def scalaDot(name: Name) = Select(Ident(nme.scala_), name) + def scalaAnnotationDot(name: Name) = Select(scalaDot(nme.annotation), name) + + def javaDot(name: Name): Tree = + Select(rootId(nme.java), name) + + def javaLangDot(name: Name): Tree = + Select(javaDot(nme.lang), name) + + def javaLangObject(): Tree = javaLangDot(tpnme.Object) + + def arrayOf(tpt: Tree) = + AppliedTypeTree(Ident(nme.Array.toTypeName), List(tpt)) + + def unimplementedExpr = Ident("???".toTermName) + + def makePackaging(pkg: RefTree, stats: List[Tree]): PackageDef = + atPos(pkg.pos) { PackageDef(pkg, stats) } + + def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef]) = { + def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match { + case (meth: DefDef) :: rest if meth.name.isConstructorName => (meth, rest) + case first :: rest => + val (constr, tail) = pullOutFirstConstr(rest) + (constr, first :: tail) + case nil => (EmptyTree, nil) + } + val (constr, stats1) = pullOutFirstConstr(stats) + val constr1 = if(constr == EmptyTree) makeConstructor(List(), tparams) else constr.asInstanceOf[DefDef] + Template(constr1, parents, EmptyValDef, stats1) + } + + def makeSyntheticParam(count: Int, tpt: Tree): ValDef = + makeParam(nme.syntheticParamName(count), tpt) + def makeParam(name: String, tpt: Tree): ValDef = + makeParam(name.toTermName, tpt) + def makeParam(name: TermName, tpt: Tree, flags: FlagSet = Flags.Param): ValDef = + ValDef(Modifiers(flags | Flags.JavaDefined), name, tpt, EmptyTree) + + def makeConstructor(formals: List[Tree], tparams: List[TypeDef]) = { + val vparams = mapWithIndex(formals)((p, i) => makeSyntheticParam(i + 1, p)) + DefDef(Modifiers(Flags.JavaDefined), nme.CONSTRUCTOR, tparams, List(vparams), TypeTree(), EmptyTree) + } + + // ------------- general parsing --------------------------- + + /** skip parent or brace enclosed sequence of things */ + def skipAhead(): Unit = { + var nparens = 0 + var nbraces = 0 + do { + in.token match { + case LPAREN => + nparens += 1 + case LBRACE => + nbraces += 1 + case _ => + } + in.nextToken() + in.token match { + case RPAREN => + nparens -= 1 + case RBRACE => + nbraces -= 1 + case _ => + } + } while (in.token != EOF && (nparens > 0 || nbraces > 0)) + } + + def skipTo(tokens: Int*): Unit = { + while (!(tokens contains in.token) && in.token != EOF) { + if (in.token == LBRACE) { skipAhead(); accept(RBRACE) } + else if (in.token == LPAREN) { skipAhead(); accept(RPAREN) } + else in.nextToken() + } + } + + /** Consume one token of the specified type, or + * signal an error if it is not there. + */ + def accept(token: Int): Int = { + val offset = in.offset + if (in.token != token) { + val offsetToReport = in.offset + val msg = + tokenString(token) + " expected but " + + tokenString(in.token) + " found." + + syntaxError(offsetToReport, msg, skipIt = true) + } + if (in.token == token) in.nextToken() + offset + } + + def acceptClosingAngle(): Unit = { + val closers: PartialFunction[Int, Int] = { + case GTGTGTEQ => GTGTEQ + case GTGTGT => GTGT + case GTGTEQ => GTEQ + case GTGT => GT + case GTEQ => EQUALS + } + if (closers isDefinedAt in.token) in.token = closers(in.token) + else accept(GT) + } + + def identForType(): TypeName = ident().toTypeName + def ident(): Name = + if (in.token == IDENTIFIER) { + val name = in.name + in.nextToken() + name + } else { + accept(IDENTIFIER) + nme.ERROR + } + + def repsep[T <: Tree](p: () => T, sep: Int): List[T] = { + val buf = ListBuffer[T](p()) + while (in.token == sep) { + in.nextToken() + buf += p() + } + buf.toList + } + + /** Convert (qual)ident to type identifier + */ + def convertToTypeId(tree: Tree): Tree = convertToTypeName(tree) match { + case Some(t) => t withPos tree.pos + case _ => tree match { + case AppliedTypeTree(_, _) | SelectFromTypeTree(_, _) => + tree + case _ => + syntaxError("identifier expected", tree.pos) + errorTypeTree + } + } + + /** Translate names in Select/Ident nodes to type names. + */ + def convertToTypeName(tree: Tree): Option[RefTree] = tree match { + case Select(qual, name) => Some(Select(qual, name.toTypeName)) + case Ident(name) => Some(Ident(name.toTypeName)) + case _ => None + } + // -------------------- specific parsing routines ------------------ + + def qualId(): RefTree = { + var t: RefTree = atPos(in.offset) { Ident(ident()) } + while (in.token == DOT) { + in.nextToken() + t = atPos(in.offset) { Select(t, ident()) } + } + t + } + + def optArrayBrackets(tpt: Tree): Tree = + if (in.token == LBRACKET) { + val tpt1 = atPos(in.offset) { arrayOf(tpt) } + in.nextToken() + accept(RBRACKET) + optArrayBrackets(tpt1) + } else tpt + + def basicType(): Tree = + atPos(in.offset) { + in.token match { + case BYTE => in.nextToken(); TypeTree(ByteType) + case SHORT => in.nextToken(); TypeTree(ShortType) + case CHAR => in.nextToken(); TypeTree(CharType) + case INT => in.nextToken(); TypeTree(IntType) + case LONG => in.nextToken(); TypeTree(LongType) + case FLOAT => in.nextToken(); TypeTree(FloatType) + case DOUBLE => in.nextToken(); TypeTree(DoubleType) + case BOOLEAN => in.nextToken(); TypeTree(BooleanType) + case _ => syntaxError("illegal start of type", skipIt = true); errorTypeTree + } + } + + def typ(): Tree = + optArrayBrackets { + if (in.token == FINAL) in.nextToken() + if (in.token == IDENTIFIER) { + var t = typeArgs(atPos(in.offset)(Ident(ident()))) + // typeSelect generates Select nodes is the lhs is an Ident or Select, + // SelectFromTypeTree otherwise. See #3567. + // Select nodes can be later + // converted in the typechecker to SelectFromTypeTree if the class + // turns out to be an instance ionner class instead of a static inner class. + def typeSelect(t: Tree, name: Name) = t match { + case Ident(_) | Select(_, _) => Select(t, name) + case _ => SelectFromTypeTree(t, name.toTypeName) + } + while (in.token == DOT) { + in.nextToken() + t = typeArgs(atPos(in.offset)(typeSelect(t, ident()))) + } + convertToTypeId(t) + } else { + basicType() + } + } + + def typeArgs(t: Tree): Tree = { + var wildnum = 0 + def typeArg(): Tree = + if (in.token == QMARK) { + val offset = in.offset + in.nextToken() + val hi = if (in.token == EXTENDS) { in.nextToken() ; typ() } else EmptyTree + val lo = if (in.token == SUPER) { in.nextToken() ; typ() } else EmptyTree + atPos(offset) { + /* + TypeDef( + Modifiers(Flags.JavaDefined | Flags.Deferred), + typeName("_$"+(wildnum += 1)), + List(), + TypeBoundsTree(lo, hi)) + */ + TypeBoundsTree(lo, hi) + } + } else { + typ() + } + if (in.token == LT) { + in.nextToken() + val t1 = convertToTypeId(t) + val args = repsep(typeArg, COMMA) + acceptClosingAngle() + atPos(t1.pos) { + AppliedTypeTree(t1, args) + } + } else t + } + + def annotations(): List[Tree] = { + //var annots = new ListBuffer[Tree] + while (in.token == AT) { + in.nextToken() + annotation() + } + List() // don't pass on annotations for now + } + + /** Annotation ::= TypeName [`(` AnnotationArgument {`,` AnnotationArgument} `)`] + */ + def annotation(): Unit = { + qualId() + if (in.token == LPAREN) { skipAhead(); accept(RPAREN) } + else if (in.token == LBRACE) { skipAhead(); accept(RBRACE) } + } + + def modifiers(inInterface: Boolean): Modifiers = { + var flags = Flags.JavaDefined + // assumed true unless we see public/private/protected + var isPackageAccess = true + var annots: List[Tree] = Nil + def addAnnot(sym: ClassSymbol) = annots :+= New(TypeTree(sym.typeRef)) + + while (true) { + in.token match { + case AT if (in.lookaheadToken != INTERFACE) => + in.nextToken() + annotation() + case PUBLIC => + isPackageAccess = false + in.nextToken() + case PROTECTED => + flags |= Flags.Protected + in.nextToken() + case PRIVATE => + isPackageAccess = false + flags |= Flags.Private + in.nextToken() + case STATIC => + flags |= Flags.Static + in.nextToken() + case ABSTRACT => + flags |= Flags.Abstract + in.nextToken() + case FINAL => + flags |= Flags.Final + in.nextToken() + case DEFAULT => + flags |= Flags.DefaultMethod + in.nextToken() + case NATIVE => + addAnnot(NativeAnnot) + in.nextToken() + case TRANSIENT => + addAnnot(TransientAnnot) + in.nextToken() + case VOLATILE => + addAnnot(VolatileAnnot) + in.nextToken() + case SYNCHRONIZED | STRICTFP => + in.nextToken() + case _ => + val privateWithin: TypeName = + if (isPackageAccess && !inInterface) thisPackageName + else tpnme.EMPTY + + return Modifiers(flags, privateWithin) withAnnotations annots + } + } + assert(false, "should not be here") + throw new RuntimeException + } + + def typeParams(flags: FlagSet = Flags.JavaDefined | Flags.PrivateLocal | Flags.Param): List[TypeDef] = + if (in.token == LT) { + in.nextToken() + val tparams = repsep(() => typeParam(flags), COMMA) + acceptClosingAngle() + tparams + } else List() + + def typeParam(flags: FlagSet): TypeDef = + atPos(in.offset) { + val name = identForType() + val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else EmptyTree + TypeDef(Modifiers(flags), name, Nil, TypeBoundsTree(EmptyTree, hi)) + } + + def bound(): Tree = + atPos(in.offset) { + val buf = ListBuffer[Tree](typ()) + while (in.token == AMP) { + in.nextToken() + buf += typ() + } + val ts = buf.toList + if (ts.tail.isEmpty) ts.head + else ts.reduce(AndTypeTree(_,_)) + } + + def formalParams(): List[ValDef] = { + accept(LPAREN) + val vparams = if (in.token == RPAREN) List() else repsep(formalParam, COMMA) + accept(RPAREN) + vparams + } + + def formalParam(): ValDef = { + if (in.token == FINAL) in.nextToken() + annotations() + var t = typ() + if (in.token == DOTDOTDOT) { + in.nextToken() + t = atPos(t.pos) { + PostfixOp(t, nme.raw.STAR) + } + } + varDecl(Position(in.offset), Modifiers(Flags.JavaDefined | Flags.Param), t, ident().toTermName, dontAddMutable = true) + } + + def optThrows(): Unit = { + if (in.token == THROWS) { + in.nextToken() + repsep(typ, COMMA) + } + } + + def methodBody(): Tree = { + skipAhead() + accept(RBRACE) // skip block + unimplementedExpr + } + + def definesInterface(token: Int) = token == INTERFACE || token == AT + + def termDecl(mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = { + val inInterface = definesInterface(parentToken) + val tparams = if (in.token == LT) typeParams(Flags.JavaDefined | Flags.Param) else List() + val isVoid = in.token == VOID + var rtpt = + if (isVoid) { + in.nextToken() + TypeTree(UnitType) withPos Position(in.offset) + } else typ() + var offset = in.offset + val rtptName = rtpt match { + case Ident(name) => name + case _ => nme.EMPTY + } + if (in.token == LPAREN && rtptName != nme.EMPTY && !inInterface) { + // constructor declaration + val vparams = formalParams() + optThrows() + List { + atPos(offset) { + DefDef(mods, nme.CONSTRUCTOR, parentTParams, List(vparams), TypeTree(), methodBody()) + } + } + } else { + var mods1 = mods + if (mods is Flags.Abstract) mods1 = mods &~ Flags.Abstract + offset = in.offset + val name = ident() + if (in.token == LPAREN) { + // method declaration + val vparams = formalParams() + if (!isVoid) rtpt = optArrayBrackets(rtpt) + optThrows() + val bodyOk = !inInterface || (mods is Flags.DefaultMethod) + val body = + if (bodyOk && in.token == LBRACE) { + methodBody() + } else { + if (parentToken == AT && in.token == DEFAULT) { + val annot = + atPos(offset) { + New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil) + } + mods1 = mods1 withAnnotations annot :: Nil + skipTo(SEMI) + accept(SEMI) + unimplementedExpr + } else { + accept(SEMI) + EmptyTree + } + } + //if (inInterface) mods1 |= Flags.Deferred + List { + atPos(offset) { + DefDef(mods1 | Flags.Method, name.toTermName, tparams, List(vparams), rtpt, body) + } + } + } else { + if (inInterface) mods1 |= Flags.Final | Flags.Static + val result = fieldDecls(Position(offset), mods1, rtpt, name) + accept(SEMI) + result + } + } + } + + /** Parse a sequence of field declarations, separated by commas. + * This one is tricky because a comma might also appear in an + * initializer. Since we don't parse initializers we don't know + * what the comma signifies. + * We solve this with a second list buffer `maybe` which contains + * potential variable definitions. + * Once we have reached the end of the statement, we know whether + * these potential definitions are real or not. + */ + def fieldDecls(pos: Position, mods: Modifiers, tpt: Tree, name: Name): List[Tree] = { + val buf = ListBuffer[Tree](varDecl(pos, mods, tpt, name.toTermName)) + val maybe = new ListBuffer[Tree] // potential variable definitions. + while (in.token == COMMA) { + in.nextToken() + if (in.token == IDENTIFIER) { // if there's an ident after the comma ... + val name = ident() + if (in.token == EQUALS || in.token == SEMI) { // ... followed by a `=` or `;`, we know it's a real variable definition + buf ++= maybe + buf += varDecl(Position(in.offset), mods, tpt, name.toTermName) + maybe.clear() + } else if (in.token == COMMA) { // ... if there's a comma after the ident, it could be a real vardef or not. + maybe += varDecl(Position(in.offset), mods, tpt, name.toTermName) + } else { // ... if there's something else we were still in the initializer of the + // previous var def; skip to next comma or semicolon. + skipTo(COMMA, SEMI) + maybe.clear() + } + } else { // ... if there's no ident following the comma we were still in the initializer of the + // previous var def; skip to next comma or semicolon. + skipTo(COMMA, SEMI) + maybe.clear() + } + } + if (in.token == SEMI) { + buf ++= maybe // every potential vardef that survived until here is real. + } + buf.toList + } + + def varDecl(pos: Position, mods: Modifiers, tpt: Tree, name: TermName, dontAddMutable: Boolean = false): ValDef = { + val tpt1 = optArrayBrackets(tpt) + if (in.token == EQUALS && !(mods is Flags.Param)) skipTo(COMMA, SEMI) + val mods1 = if (mods is Flags.Final) mods &~ Flags.Final else if(dontAddMutable) mods else mods | Flags.Mutable + atPos(pos) { + ValDef(mods1, name, tpt1, EmptyTree) + } + } + + def memberDecl(mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match { + case CLASS | ENUM | INTERFACE | AT => + typeDecl(if (definesInterface(parentToken)) mods | Flags.Static else mods) + case _ => + termDecl(mods, parentToken, parentTParams) + } + + def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree = + atPos(cdef.pos) { + ModuleDef((cdef.mods & (Flags.AccessFlags | Flags.JavaDefined)).toTermFlags, cdef.name.toTermName, + makeTemplate(List(), statics, List())) + } + + private val wild = Ident(nme.WILDCARD) withPos Position(-1) + private val wildList = List(wild) // OPT This list is shared for performance. + + def importCompanionObject(cdef: TypeDef): Tree = + atPos(cdef.pos) { + Import(Ident(cdef.name.toTermName), wildList) + } + + // Importing the companion object members cannot be done uncritically: see + // ticket #2377 wherein a class contains two static inner classes, each of which + // has a static inner class called "Builder" - this results in an ambiguity error + // when each performs the import in the enclosing class's scope. + // + // To address this I moved the import Companion._ inside the class, as the first + // statement. This should work without compromising the enclosing scope, but may (?) + // end up suffering from the same issues it does in scala - specifically that this + // leaves auxiliary constructors unable to access members of the companion object + // as unqualified identifiers. + def addCompanionObject(statics: List[Tree], cdef: TypeDef): List[Tree] = { + // if there are no statics we can use the original cdef, but we always + // create the companion so import A._ is not an error (see ticket #1700) + val cdefNew = + if (statics.isEmpty) cdef + else { + val template = cdef.rhs.asInstanceOf[Template] + cpy.TypeDef(cdef, cdef.mods, cdef.name, + cpy.Template(template, template.constr, template.parents, template.self, + importCompanionObject(cdef) :: template.body), + cdef.tparams) + } + + List(makeCompanionObject(cdefNew, statics), cdefNew) + } + + def importDecl(): List[Tree] = { + accept(IMPORT) + val offset = in.offset + val buf = new ListBuffer[Name] + def collectIdents() : Int = { + if (in.token == ASTERISK) { + val starOffset = in.offset + in.nextToken() + buf += nme.WILDCARD + starOffset + } else { + val nameOffset = in.offset + buf += ident() + if (in.token == DOT) { + in.nextToken() + collectIdents() + } else nameOffset + } + } + if (in.token == STATIC) in.nextToken() + else buf += nme.ROOTPKG + val lastnameOffset = collectIdents() + accept(SEMI) + val names = buf.toList + if (names.length < 2) { + syntaxError(offset, "illegal import", skipIt = false) + List() + } else { + val qual = ((Ident(names.head): Tree) /: names.tail.init) (Select(_, _)) + val lastname = names.last + val ident = Ident(lastname) withPos Position(lastnameOffset) +// val selector = lastname match { +// case nme.WILDCARD => Pair(ident, Ident(null) withPos Position(-1)) +// case _ => Pair(ident, ident) +// } + List(atPos(offset)(Import(qual, List(ident)))) + } + } + + def interfacesOpt() = + if (in.token == IMPLEMENTS) { + in.nextToken() + repsep(typ, COMMA) + } else { + List() + } + + def classDecl(mods: Modifiers): List[Tree] = { + accept(CLASS) + val offset = in.offset + val name = identForType() + val tparams = typeParams() + val superclass = + if (in.token == EXTENDS) { + in.nextToken() + typ() + } else { + javaLangObject() + } + val interfaces = interfacesOpt() + val (statics, body) = typeBody(CLASS, name, tparams) + addCompanionObject(statics, atPos(offset) { + TypeDef(mods, name, makeTemplate(superclass :: interfaces, body, tparams)) + }) + } + + def interfaceDecl(mods: Modifiers): List[Tree] = { + accept(INTERFACE) + val offset = in.offset + val name = identForType() + val tparams = typeParams() + val parents = + if (in.token == EXTENDS) { + in.nextToken() + repsep(typ, COMMA) + } else { + List(javaLangObject()) + } + val (statics, body) = typeBody(INTERFACE, name, tparams) + addCompanionObject(statics, atPos(offset) { + TypeDef(mods | Flags.Trait | Flags.Interface | Flags.Abstract, + name, tparams, + makeTemplate(parents, body, tparams)) + }) + } + + def typeBody(leadingToken: Int, parentName: Name, parentTParams: List[TypeDef]): (List[Tree], List[Tree]) = { + accept(LBRACE) + val defs = typeBodyDecls(leadingToken, parentName, parentTParams) + accept(RBRACE) + defs + } + + def typeBodyDecls(parentToken: Int, parentName: Name, parentTParams: List[TypeDef]): (List[Tree], List[Tree]) = { + val inInterface = definesInterface(parentToken) + val statics = new ListBuffer[Tree] + val members = new ListBuffer[Tree] + while (in.token != RBRACE && in.token != EOF) { + var mods = modifiers(inInterface) + if (in.token == LBRACE) { + skipAhead() // skip init block, we just assume we have seen only static + accept(RBRACE) + } else if (in.token == SEMI) { + in.nextToken() + } else { + if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.Static + val decls = memberDecl(mods, parentToken, parentTParams) + (if ((mods is Flags.Static) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) + statics + else + members) ++= decls + } + } + def forwarders(sdef: Tree): List[Tree] = sdef match { + case TypeDef(mods, name, _) if (parentToken == INTERFACE) => + var rhs: Tree = Select(Ident(parentName.toTermName), name) + List(TypeDef(Modifiers(Flags.Protected), name, rhs)) + case _ => + List() + } + val sdefs = statics.toList + val idefs = members.toList ::: (sdefs flatMap forwarders) + (sdefs, idefs) + } + def annotationParents = List( + scalaAnnotationDot(tpnme.Annotation), + Select(javaLangDot(nme.annotation), tpnme.Annotation), + scalaAnnotationDot(tpnme.ClassfileAnnotation) + ) + def annotationDecl(mods: Modifiers): List[Tree] = { + accept(AT) + accept(INTERFACE) + val offset = in.offset + val name = identForType() + val (statics, body) = typeBody(AT, name, List()) + val constructorParams = body.collect { + case dd: DefDef => makeParam(dd.name, dd.tpt, Flags.PrivateLocalParamAccessor) + } + val constr = DefDef(Modifiers(Flags.JavaDefined), nme.CONSTRUCTOR, + List(), List(constructorParams), TypeTree(), EmptyTree) + val body1 = body.filterNot(_.isInstanceOf[DefDef]) + val templ = makeTemplate(annotationParents, constr :: body1, List()) + addCompanionObject(statics, atPos(offset) { + TypeDef(mods, name, templ) + }) + } + + def enumDecl(mods: Modifiers): List[Tree] = { + accept(ENUM) + val offset = in.offset + val name = identForType() + def enumType = Ident(name) + val interfaces = interfacesOpt() + accept(LBRACE) + val buf = new ListBuffer[Tree] + def parseEnumConsts(): Unit = { + if (in.token != RBRACE && in.token != SEMI && in.token != EOF) { + buf += enumConst(enumType) + if (in.token == COMMA) { + in.nextToken() + parseEnumConsts() + } + } + } + parseEnumConsts() + val consts = buf.toList + val (statics, body) = + if (in.token == SEMI) { + in.nextToken() + typeBodyDecls(ENUM, name, List()) + } else { + (List(), List()) + } + val predefs = List( + DefDef( + Modifiers(Flags.JavaDefined | Flags.Static | Flags.Method), nme.values, List(), + ListOfNil, + arrayOf(enumType), + unimplementedExpr), + DefDef( + Modifiers(Flags.JavaDefined | Flags.Static | Flags.Method), nme.valueOf, List(), + List(List(makeParam("x", TypeTree(StringType)))), + enumType, + unimplementedExpr)) + accept(RBRACE) + /* + val superclazz = + AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType)) + */ + val superclazz = Apply(TypeApply( + Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), + List(Literal(Constant(null)),Literal(Constant(0)))) + addCompanionObject(consts ::: statics ::: predefs, atPos(offset) { + TypeDef(mods | Flags.Enum, name, List(), + makeTemplate(superclazz :: interfaces, body, List())) + }) + } + + def enumConst(enumType: Tree) = { + annotations() + atPos(in.offset) { + val name = ident() + if (in.token == LPAREN) { + // skip arguments + skipAhead() + accept(RPAREN) + } + if (in.token == LBRACE) { + // skip classbody + skipAhead() + accept(RBRACE) + } + ValDef(Modifiers(Flags.Enum | Flags.Stable | Flags.JavaDefined | Flags.Static), name.toTermName, enumType, unimplementedExpr) + } + } + + def typeDecl(mods: Modifiers): List[Tree] = in.token match { + case ENUM => enumDecl(mods) + case INTERFACE => interfaceDecl(mods) + case AT => annotationDecl(mods) + case CLASS => classDecl(mods) + case _ => in.nextToken(); syntaxError("illegal start of type declaration", skipIt = true); List(errorTypeTree) + } + + /** CompilationUnit ::= [package QualId semi] TopStatSeq + */ + def compilationUnit(): Tree = { + var offset = in.offset + val pkg: RefTree = + if (in.token == AT || in.token == PACKAGE) { + annotations() + offset = in.offset + accept(PACKAGE) + val pkg = qualId() + accept(SEMI) + pkg + } else { + Ident(nme.EMPTY_PACKAGE) + } + thisPackageName = convertToTypeName(pkg) match { + case Some(t) => t.name.toTypeName + case _ => tpnme.EMPTY + } + val buf = new ListBuffer[Tree] + while (in.token == IMPORT) + buf ++= importDecl() + while (in.token != EOF && in.token != RBRACE) { + while (in.token == SEMI) in.nextToken() + if (in.token != EOF) + buf ++= typeDecl(modifiers(inInterface = false)) + } + accept(EOF) + atPos(offset) { + makePackaging(pkg, buf.toList) + } + } + } +} diff --git a/src/dotty/tools/dotc/parsing/JavaScanners.scala b/src/dotty/tools/dotc/parsing/JavaScanners.scala new file mode 100644 index 000000000000..faac8e1638ca --- /dev/null +++ b/src/dotty/tools/dotc/parsing/JavaScanners.scala @@ -0,0 +1,537 @@ +package dotty.tools +package dotc +package parsing + +import core.Names._, core.Contexts._, core.Decorators._, util.Positions._ +import Scanners._ +import util.SourceFile +import JavaTokens._ +import scala.annotation.{ switch, tailrec } +import scala.reflect.internal.Chars._ + +object JavaScanners { + + class JavaScanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) { + + def toToken(idx: Int): Token = + if (idx >= 0 && idx <= lastKeywordStart) kwArray(idx) else IDENTIFIER + + private class JavaTokenData0 extends TokenData + + /** we need one token lookahead + */ + val next : TokenData = new JavaTokenData0 + val prev : TokenData = new JavaTokenData0 + + // Get next token ------------------------------------------------------------ + + def nextToken(): Unit = { + if (next.token == EMPTY) { + fetchToken() + } + else { + this copyFrom next + next.token = EMPTY + } + } + + def lookaheadToken: Int = { + prev copyFrom this + nextToken() + val t = token + next copyFrom this + this copyFrom prev + t + } + + /** read next token + */ + private def fetchToken(): Unit = { + offset = charOffset - 1 + ch match { + case ' ' | '\t' | CR | LF | FF => + nextChar() + fetchToken() + case _ => + (ch: @switch) match { + case 'A' | 'B' | 'C' | 'D' | 'E' | + 'F' | 'G' | 'H' | 'I' | 'J' | + 'K' | 'L' | 'M' | 'N' | 'O' | + 'P' | 'Q' | 'R' | 'S' | 'T' | + 'U' | 'V' | 'W' | 'X' | 'Y' | + 'Z' | '$' | '_' | + 'a' | 'b' | 'c' | 'd' | 'e' | + 'f' | 'g' | 'h' | 'i' | 'j' | + 'k' | 'l' | 'm' | 'n' | 'o' | + 'p' | 'q' | 'r' | 's' | 't' | + 'u' | 'v' | 'w' | 'x' | 'y' | + 'z' => + putChar(ch) + nextChar() + getIdentRest() + + case '0' => + putChar(ch) + nextChar() + if (ch == 'x' || ch == 'X') { + nextChar() + base = 16 + } else { + base = 8 + } + getNumber() + + case '1' | '2' | '3' | '4' | + '5' | '6' | '7' | '8' | '9' => + base = 10 + getNumber() + + case '\"' => + nextChar() + while (ch != '\"' && (isUnicodeEscape || ch != CR && ch != LF && ch != SU)) { + getlitch() + } + if (ch == '\"') { + token = STRINGLIT + setStrVal() + nextChar() + } else { + error("unclosed string literal") + } + + case '\'' => + nextChar() + getlitch() + if (ch == '\'') { + nextChar() + token = CHARLIT + setStrVal() + } else { + error("unclosed character literal") + } + + case '=' => + token = EQUALS + nextChar() + if (ch == '=') { + token = EQEQ + nextChar() + } + + case '>' => + token = GT + nextChar() + if (ch == '=') { + token = GTEQ + nextChar() + } else if (ch == '>') { + token = GTGT + nextChar() + if (ch == '=') { + token = GTGTEQ + nextChar() + } else if (ch == '>') { + token = GTGTGT + nextChar() + if (ch == '=') { + token = GTGTGTEQ + nextChar() + } + } + } + + case '<' => + token = LT + nextChar() + if (ch == '=') { + token = LTEQ + nextChar() + } else if (ch == '<') { + token = LTLT + nextChar() + if (ch == '=') { + token = LTLTEQ + nextChar() + } + } + + case '!' => + token = BANG + nextChar() + if (ch == '=') { + token = BANGEQ + nextChar() + } + + case '~' => + token = TILDE + nextChar() + + case '?' => + token = QMARK + nextChar() + + case ':' => + token = COLON + nextChar() + + case '@' => + token = AT + nextChar() + + case '&' => + token = AMP + nextChar() + if (ch == '&') { + token = AMPAMP + nextChar() + } else if (ch == '=') { + token = AMPEQ + nextChar() + } + + case '|' => + token = BAR + nextChar() + if (ch == '|') { + token = BARBAR + nextChar() + } else if (ch == '=') { + token = BAREQ + nextChar() + } + + case '+' => + token = PLUS + nextChar() + if (ch == '+') { + token = PLUSPLUS + nextChar() + } else if (ch == '=') { + token = PLUSEQ + nextChar() + } + + case '-' => + token = MINUS + nextChar() + if (ch == '-') { + token = MINUSMINUS + nextChar() + } else if (ch == '=') { + token = MINUSEQ + nextChar() + } + + case '*' => + token = ASTERISK + nextChar() + if (ch == '=') { + token = ASTERISKEQ + nextChar() + } + + case '/' => + nextChar() + if (!skipComment()) { + token = SLASH + nextChar() + if (ch == '=') { + token = SLASHEQ + nextChar() + } + } else fetchToken() + + case '^' => + token = HAT + nextChar() + if (ch == '=') { + token = HATEQ + nextChar() + } + + case '%' => + token = PERCENT + nextChar() + if (ch == '=') { + token = PERCENTEQ + nextChar() + } + + case '.' => + token = DOT + nextChar() + if ('0' <= ch && ch <= '9') { + putChar('.'); + getFraction() + } else if (ch == '.') { + nextChar() + if (ch == '.') { + nextChar() + token = DOTDOTDOT + } else error("`.' character expected") + } + + case ';' => + token = SEMI + nextChar() + + case ',' => + token = COMMA + nextChar() + + case '(' => + token = LPAREN + nextChar() + + case '{' => + token = LBRACE + nextChar() + + case ')' => + token = RPAREN + nextChar() + + case '}' => + token = RBRACE + nextChar() + + case '[' => + token = LBRACKET + nextChar() + + case ']' => + token = RBRACKET + nextChar() + + case SU => + if (isAtEnd) token = EOF + else { + error("illegal character") + nextChar() + } + + case _ => + if (Character.isUnicodeIdentifierStart(ch)) { + putChar(ch) + nextChar() + getIdentRest() + } else { + error("illegal character: " + ch.toInt) + nextChar() + } + } + } + } + + protected def skipComment(): Boolean = { + @tailrec def skipLineComment(): Unit = ch match { + case CR | LF | SU => + case _ => nextChar(); skipLineComment() + } + @tailrec def skipJavaComment(): Unit = ch match { + case SU => incompleteInputError("unclosed comment") + case '*' => nextChar(); if (ch == '/') nextChar() else skipJavaComment() + case _ => nextChar(); skipJavaComment() + } + ch match { + case '/' => nextChar(); skipLineComment(); true + case '*' => nextChar(); skipJavaComment(); true + case _ => false + } + } + + // Identifiers --------------------------------------------------------------- + + private def getIdentRest(): Unit = { + while (true) { + (ch: @switch) match { + case 'A' | 'B' | 'C' | 'D' | 'E' | + 'F' | 'G' | 'H' | 'I' | 'J' | + 'K' | 'L' | 'M' | 'N' | 'O' | + 'P' | 'Q' | 'R' | 'S' | 'T' | + 'U' | 'V' | 'W' | 'X' | 'Y' | + 'Z' | '$' | + 'a' | 'b' | 'c' | 'd' | 'e' | + 'f' | 'g' | 'h' | 'i' | 'j' | + 'k' | 'l' | 'm' | 'n' | 'o' | + 'p' | 'q' | 'r' | 's' | 't' | + 'u' | 'v' | 'w' | 'x' | 'y' | + 'z' | + '0' | '1' | '2' | '3' | '4' | + '5' | '6' | '7' | '8' | '9' => + putChar(ch) + nextChar() + + case '_' => + putChar(ch) + nextChar() + getIdentRest() + return + case SU => + finishNamed() + return + case _ => + if (Character.isUnicodeIdentifierPart(ch)) { + putChar(ch) + nextChar() + } else { + finishNamed() + return + } + } + } + } + + // Literals ----------------------------------------------------------------- + + /** read next character in character or string literal: + */ + protected def getlitch() = + if (ch == '\\') { + nextChar() + if ('0' <= ch && ch <= '7') { + val leadch: Char = ch + var oct: Int = digit2int(ch, 8) + nextChar() + if ('0' <= ch && ch <= '7') { + oct = oct * 8 + digit2int(ch, 8) + nextChar() + if (leadch <= '3' && '0' <= ch && ch <= '7') { + oct = oct * 8 + digit2int(ch, 8) + nextChar() + } + } + putChar(oct.asInstanceOf[Char]) + } else { + ch match { + case 'b' => putChar('\b') + case 't' => putChar('\t') + case 'n' => putChar('\n') + case 'f' => putChar('\f') + case 'r' => putChar('\r') + case '\"' => putChar('\"') + case '\'' => putChar('\'') + case '\\' => putChar('\\') + case _ => + error("invalid escape character", charOffset - 1) + putChar(ch) + } + nextChar() + } + } else { + putChar(ch) + nextChar() + } + + /** read fractional part and exponent of floating point number + * if one is present. + */ + protected def getFraction(): Unit = { + token = DOUBLELIT + while ('0' <= ch && ch <= '9') { + putChar(ch) + nextChar() + } + if (ch == 'e' || ch == 'E') { + val lookahead = lookaheadReader + lookahead.nextChar() + if (lookahead.ch == '+' || lookahead.ch == '-') { + lookahead.nextChar() + } + if ('0' <= lookahead.ch && lookahead.ch <= '9') { + putChar(ch) + nextChar() + if (ch == '+' || ch == '-') { + putChar(ch) + nextChar() + } + while ('0' <= ch && ch <= '9') { + putChar(ch) + nextChar() + } + } + token = DOUBLELIT + } + if (ch == 'd' || ch == 'D') { + putChar(ch) + nextChar() + token = DOUBLELIT + } else if (ch == 'f' || ch == 'F') { + putChar(ch) + nextChar() + token = FLOATLIT + } + setStrVal() + } + + /** read a number into name and set base + */ + protected def getNumber(): Unit = { + while (digit2int(ch, if (base < 10) 10 else base) >= 0) { + putChar(ch) + nextChar() + } + token = INTLIT + if (base <= 10 && ch == '.') { + val lookahead = lookaheadReader + lookahead.nextChar() + lookahead.ch match { + case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | + '8' | '9' | 'd' | 'D' | 'e' | 'E' | 'f' | 'F' => + putChar(ch) + nextChar() + return getFraction() + case _ => + if (!isIdentifierStart(lookahead.ch)) { + putChar(ch) + nextChar() + return getFraction() + } + } + } + if (base <= 10 && + (ch == 'e' || ch == 'E' || + ch == 'f' || ch == 'F' || + ch == 'd' || ch == 'D')) { + return getFraction() + } + setStrVal() + if (ch == 'l' || ch == 'L') { + nextChar() + token = LONGLIT + } + } + + // Errors ----------------------------------------------------------------- + + override def toString() = token match { + case IDENTIFIER => + "id(" + name + ")" + case CHARLIT => + "char(" + intVal + ")" + case INTLIT => + "int(" + intVal + ")" + case LONGLIT => + "long(" + intVal + ")" + case FLOATLIT => + "float(" + floatVal + ")" + case DOUBLELIT => + "double(" + floatVal + ")" + case STRINGLIT => + "string(" + name + ")" + case SEMI => + ";" + case COMMA => + "," + case _ => + tokenString(token) + } + + /* Initialization: read first char, then first token */ + nextChar() + nextToken() + } + + val (lastKeywordStart, kwArray) = buildKeywordArray(keywords) +} diff --git a/src/dotty/tools/dotc/parsing/JavaTokens.scala b/src/dotty/tools/dotc/parsing/JavaTokens.scala new file mode 100644 index 000000000000..9530e0516196 --- /dev/null +++ b/src/dotty/tools/dotc/parsing/JavaTokens.scala @@ -0,0 +1,92 @@ +package dotty.tools +package dotc +package parsing + +import collection.immutable.BitSet + +object JavaTokens extends TokensCommon { + final val minToken = EMPTY + final val maxToken = DOUBLE + + final val javaOnlyKeywords = tokenRange(INSTANCEOF, ASSERT) + final val sharedKeywords = BitSet( IF, FOR, ELSE, THIS, NULL, NEW, SUPER, ABSTRACT, FINAL, PRIVATE, PROTECTED, + OVERRIDE, EXTENDS, TRUE, FALSE, CLASS, IMPORT, PACKAGE, DO, THROW, TRY, CATCH, FINALLY, WHILE, RETURN ) + final val primTypes = tokenRange(VOID, DOUBLE) + final val keywords = sharedKeywords | javaOnlyKeywords | primTypes + + /** keywords */ + final val INSTANCEOF = 101; enter(INSTANCEOF, "instanceof") + final val CONST = 102; enter(CONST, "const") + + /** templates */ + final val INTERFACE = 105; enter(INTERFACE, "interface") + final val ENUM = 106; enter(ENUM, "enum") + final val IMPLEMENTS = 107; enter(IMPLEMENTS, "implements") + + /** modifiers */ + final val PUBLIC = 110; enter(PUBLIC, "public") + final val DEFAULT = 111; enter(DEFAULT, "default") + final val STATIC = 112; enter(STATIC, "static") + final val TRANSIENT = 113; enter(TRANSIENT, "transient") + final val VOLATILE = 114; enter(VOLATILE, "volatile") + final val SYNCHRONIZED = 115; enter(SYNCHRONIZED, "synchronized") + final val NATIVE = 116; enter(NATIVE, "native") + final val STRICTFP = 117; enter(STRICTFP, "strictfp") + final val THROWS = 118; enter(THROWS, "throws") + + /** control structures */ + final val BREAK = 130; enter(BREAK, "break") + final val CONTINUE = 131; enter(CONTINUE, "continue") + final val GOTO = 132; enter(GOTO, "goto") + final val SWITCH = 133; enter(SWITCH, "switch") + final val ASSERT = 134; enter(ASSERT, "assert") + + /** special symbols */ + final val EQEQ = 140 + final val BANGEQ = 141 + final val LT = 142 + final val GT = 143 + final val LTEQ = 144 + final val GTEQ = 145 + final val BANG = 146 + final val QMARK = 147 + final val AMP = 148 + final val BAR = 149 + final val PLUS = 150 + final val MINUS = 151 + final val ASTERISK = 152 + final val SLASH = 153 + final val PERCENT = 154 + final val HAT = 155 + final val LTLT = 156 + final val GTGT = 157 + final val GTGTGT = 158 + final val AMPAMP = 159 + final val BARBAR = 160 + final val PLUSPLUS = 161 + final val MINUSMINUS = 162 + final val TILDE = 163 + final val DOTDOTDOT = 164 + final val AMPEQ = 165 + final val BAREQ = 166 + final val PLUSEQ = 167 + final val MINUSEQ = 168 + final val ASTERISKEQ = 169 + final val SLASHEQ = 170 + final val PERCENTEQ = 171 + final val HATEQ = 172 + final val LTLTEQ = 173 + final val GTGTEQ = 174 + final val GTGTGTEQ = 175 + + /** primitive types */ + final val VOID = 180; enter(VOID, "void") + final val BOOLEAN = 181; enter(BOOLEAN, "boolean") + final val BYTE = 182; enter(BYTE, "byte") + final val SHORT = 183; enter(SHORT, "short") + final val CHAR = 184; enter(CHAR, "char") + final val INT = 185; enter(INT, "int") + final val LONG = 186; enter(LONG, "long") + final val FLOAT = 187; enter(FLOAT, "float") + final val DOUBLE = 188; enter(DOUBLE, "double") +} diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 1efc2d31ca78..3ffd6645d58a 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -50,9 +50,56 @@ object Parsers { if (source.isSelfContained) new ScriptParser(source) else new Parser(source) - class Parser(val source: SourceFile)(implicit ctx: Context) extends DotClass { + abstract class ParserCommon(val source: SourceFile)(implicit ctx: Context) extends DotClass { - val in = new Scanner(source) + val in: ScannerCommon + + /* ------------- POSITIONS ------------------------------------------- */ + + def atPos[T <: Positioned](start: Offset, point: Offset, end: Offset)(t: T): T = + atPos(Position(start, end, point))(t) + + def atPos[T <: Positioned](start: Offset, point: Offset)(t: T): T = + atPos(start, point, in.lastOffset)(t) + + def atPos[T <: Positioned](start: Offset)(t: T): T = + atPos(start, start)(t) + + def atPos[T <: Positioned](pos: Position)(t: T): T = + if (t.pos.isSourceDerived) t else t.withPos(pos) + + def tokenRange = Position(in.offset, in.lastCharOffset, in.offset) + + def sourcePos(off: Int = in.offset): SourcePosition = + source atPos Position(off) + + + /* ------------- ERROR HANDLING ------------------------------------------- */ + /** The offset where the last syntax error was reported, or if a skip to a + * safepoint occurred afterwards, the offset of the safe point. + */ + protected var lastErrorOffset : Int = -1 + + /** Issue an error at given offset if beyond last error offset + * and update lastErrorOffset. + */ + def syntaxError(msg: String, offset: Int = in.offset): Unit = + if (offset > lastErrorOffset) { + syntaxError(msg, Position(offset)) + lastErrorOffset = in.offset + } + + /** Unconditionally issue an error at given position, without + * updating lastErrorOffset. + */ + def syntaxError(msg: String, pos: Position): Unit = + ctx.error(msg, source atPos pos) + + } + + class Parser(source: SourceFile)(implicit ctx: Context) extends ParserCommon(source) { + + val in: Scanner = new Scanner(source) val openParens = new ParensCounters @@ -84,25 +131,6 @@ object Parsers { def isStatSep: Boolean = in.token == NEWLINE || in.token == NEWLINES || in.token == SEMI -/* ------------- POSITIONS ------------------------------------------- */ - - def atPos[T <: Positioned](start: Offset, point: Offset, end: Offset)(t: T): T = - atPos(Position(start, end, point))(t) - - def atPos[T <: Positioned](start: Offset, point: Offset)(t: T): T = - atPos(start, point, in.lastOffset)(t) - - def atPos[T <: Positioned](start: Offset)(t: T): T = - atPos(start, start)(t) - - def atPos[T <: Positioned](pos: Position)(t: T): T = - if (t.pos.isSourceDerived) t else t.withPos(pos) - - def tokenRange = Position(in.offset, in.lastCharOffset, in.offset) - - def sourcePos(off: Int = in.offset): SourcePosition = - source atPos Position(off) - /* ------------- ERROR HANDLING ------------------------------------------- */ /** The offset of the last time when a statement on a new line was definitely @@ -176,26 +204,6 @@ object Parsers { def deprecationWarning(msg: String, offset: Int = in.offset) = ctx.deprecationWarning(msg, source atPos Position(offset)) - /** The offset where the last syntax error was reported, or if a skip to a - * safepoint occurred afterwards, the offset of the safe point. - */ - private var lastErrorOffset : Int = -1 - - /** Issue an error at given offset if beyond last error offset - * and update lastErrorOffset. - */ - def syntaxError(msg: String, offset: Int = in.offset): Unit = - if (offset > lastErrorOffset) { - syntaxError(msg, Position(offset)) - lastErrorOffset = in.offset - } - - /** Unconditionally issue an error at given position, without - * updating lastErrorOffset. - */ - def syntaxError(msg: String, pos: Position): Unit = - ctx.error(msg, source atPos pos) - /** Issue an error at current offset taht input is incomplete */ def incompleteInputError(msg: String) = ctx.incompleteInputError(msg, source atPos Position(in.offset)) diff --git a/src/dotty/tools/dotc/parsing/Scanners.scala b/src/dotty/tools/dotc/parsing/Scanners.scala index 4d8fdd10de4d..5eb8357a41ff 100644 --- a/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/src/dotty/tools/dotc/parsing/Scanners.scala @@ -58,36 +58,136 @@ object Scanners { } } - class Scanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends CharArrayReader with TokenData { - + abstract class ScannerCommon(source: SourceFile)(implicit ctx: Context) extends CharArrayReader with TokenData { val buf = source.content - var keepComments = false - - /** All comments in the reverse order of their position in the source. - * set only when `keepComments` is true. - */ - var revComments: List[Comment] = Nil + // Errors ----------------------------------------------------------------- /** the last error offset - */ + */ var errOffset: Offset = NoOffset - /** A buffer for comments */ - val commentBuf = new StringBuilder + + /** Generate an error at the given offset */ + def error(msg: String, off: Offset = offset) = { + ctx.error(msg, source atPos Position(off)) + token = ERROR + errOffset = off + } + + /** signal an error where the input ended in the middle of a token */ + def incompleteInputError(msg: String): Unit = { + ctx.incompleteInputError(msg, source atPos Position(offset)) + token = EOF + errOffset = offset + } + + // Setting token data ---------------------------------------------------- /** A character buffer for literals - */ + */ val litBuf = new StringBuilder /** append Unicode character to "litBuf" buffer - */ + */ protected def putChar(c: Char): Unit = litBuf.append(c) + /** Return buffer contents and clear */ + def flushBuf(buf: StringBuilder): String = { + val str = buf.toString + buf.clear() + str + } + + /** Clear buffer and set name and token */ + def finishNamed(idtoken: Token = IDENTIFIER, target: TokenData = this): Unit = { + target.name = flushBuf(litBuf).toTermName + target.token = idtoken + if (idtoken == IDENTIFIER) { + val idx = target.name.start + target.token = toToken(idx) + } + } + def toToken(idx: Int): Token + /** Clear buffer and set string */ - private def setStrVal() = + def setStrVal() = strVal = flushBuf(litBuf) + /** Convert current strVal to char value + */ + def charVal: Char = if (strVal.length > 0) strVal.charAt(0) else 0 + + /** Convert current strVal, base to long value + * This is tricky because of max negative value. + */ + def intVal(negated: Boolean): Long = { + if (token == CHARLIT && !negated) { + charVal + } else { + var value: Long = 0 + val divider = if (base == 10) 1 else 2 + val limit: Long = + if (token == LONGLIT) Long.MaxValue else Int.MaxValue + var i = 0 + val len = strVal.length + while (i < len) { + val d = digit2int(strVal charAt i, base) + if (d < 0) { + error("malformed integer number") + return 0 + } + if (value < 0 || + limit / (base / divider) < value || + limit - (d / divider) < value * (base / divider) && + !(negated && limit == value * base - 1 + d)) { + error("integer number too large") + return 0 + } + value = value * base + d + i += 1 + } + if (negated) -value else value + } + } + + def intVal: Long = intVal(false) + + /** Convert current strVal, base to double value + */ + def floatVal(negated: Boolean): Double = { + val limit: Double = + if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue + try { + val value: Double = java.lang.Double.valueOf(strVal).doubleValue() + if (value > limit) + error("floating point number too large") + if (negated) -value else value + } catch { + case _: NumberFormatException => + error("malformed floating point number") + 0.0 + } + } + + def floatVal: Double = floatVal(false) + + } + + class Scanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) { + var keepComments = false + + /** All comments in the reverse order of their position in the source. + * set only when `keepComments` is true. + */ + var revComments: List[Comment] = Nil + + /** A buffer for comments */ + val commentBuf = new StringBuilder + + def toToken(idx: Int): Token = + if (idx >= 0 && idx <= lastKeywordStart) kwArray(idx) else IDENTIFIER + private class TokenData0 extends TokenData /** we need one token lookahead and one token history @@ -818,84 +918,6 @@ object Scanners { strVal = name.toString } } - -// Setting token data ---------------------------------------------------- - - /** Clear buffer and set name and token */ - def finishNamed(idtoken: Token = IDENTIFIER, target: TokenData = this): Unit = { - target.name = flushBuf(litBuf).toTermName - target.token = idtoken - if (idtoken == IDENTIFIER) { - val idx = target.name.start - if (idx >= 0 && idx <= lastKeywordStart) target.token = kwArray(idx) - } - } - - /** Return buffer contents and clear */ - def flushBuf(buf: StringBuilder): String = { - val str = buf.toString - buf.clear() - str - } - - /** Convert current strVal to char value - */ - def charVal: Char = if (strVal.length > 0) strVal.charAt(0) else 0 - - /** Convert current strVal, base to long value - * This is tricky because of max negative value. - */ - def intVal(negated: Boolean): Long = { - if (token == CHARLIT && !negated) { - charVal - } else { - var value: Long = 0 - val divider = if (base == 10) 1 else 2 - val limit: Long = - if (token == LONGLIT) Long.MaxValue else Int.MaxValue - var i = 0 - val len = strVal.length - while (i < len) { - val d = digit2int(strVal charAt i, base) - if (d < 0) { - error("malformed integer number") - return 0 - } - if (value < 0 || - limit / (base / divider) < value || - limit - (d / divider) < value * (base / divider) && - !(negated && limit == value * base - 1 + d)) { - error("integer number too large") - return 0 - } - value = value * base + d - i += 1 - } - if (negated) -value else value - } - } - - def intVal: Long = intVal(false) - - /** Convert current strVal, base to double value - */ - def floatVal(negated: Boolean): Double = { - val limit: Double = - if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue - try { - val value: Double = java.lang.Double.valueOf(strVal).doubleValue() - if (value > limit) - error("floating point number too large") - if (negated) -value else value - } catch { - case _: NumberFormatException => - error("malformed floating point number") - 0.0 - } - } - - def floatVal: Double = floatVal(false) - override def toString = showTokenDetailed(token) + { if ((identifierTokens contains token) || (literalTokens contains token)) " " + name @@ -930,22 +952,6 @@ object Scanners { nextToken() } -// Errors ----------------------------------------------------------------- - - /** Generate an error at the given offset */ - def error(msg: String, off: Offset = offset) = { - ctx.error(msg, source atPos Position(off)) - token = ERROR - errOffset = off - } - - /** signal an error where the input ended in the middle of a token */ - def incompleteInputError(msg: String): Unit = { - ctx.incompleteInputError(msg, source atPos Position(offset)) - token = EOF - errOffset = offset - } - /* Initialization: read first char, then first token */ nextChar() nextToken() @@ -953,14 +959,5 @@ object Scanners { // ------------- keyword configuration ----------------------------------- - private def start(tok: Token) = tokenString(tok).toTermName.start - private def sourceKeywords = keywords.toList.filterNot(kw => tokenString(kw) contains ' ') - - private val lastKeywordStart = sourceKeywords.map(start).max - - private val kwArray: Array[Token] = { - val arr = Array.fill(lastKeywordStart + 1)(IDENTIFIER) - for (kw <- sourceKeywords) arr(start(kw)) = kw - arr - } + val (lastKeywordStart, kwArray) = buildKeywordArray(keywords) } diff --git a/src/dotty/tools/dotc/parsing/Tokens.scala b/src/dotty/tools/dotc/parsing/Tokens.scala index 09124d0d1886..226a3710db2b 100644 --- a/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/src/dotty/tools/dotc/parsing/Tokens.scala @@ -3,11 +3,10 @@ package dotc package parsing import collection.immutable.BitSet +import core.Decorators._ -object Tokens { - - final val minToken = EMPTY - final val maxToken = XMLSTART +abstract class TokensCommon { + val maxToken: Int type Token = Int type TokenSet = BitSet @@ -24,6 +23,7 @@ object Tokens { val tokenString, debugString = new Array[String](maxToken + 1) def enter(token: Int, str: String, debugStr: String = ""): Unit = { + assert(tokenString(token) == null) tokenString(token) = str debugString(token) = if (debugStr.isEmpty) str else debugStr } @@ -41,17 +41,12 @@ object Tokens { final val DOUBLELIT = 7; enter(DOUBLELIT, "double literal") final val STRINGLIT = 8; enter(STRINGLIT, "string literal") final val STRINGPART = 9; enter(STRINGPART, "string literal", "string literal part") - final val INTERPOLATIONID = 10; enter(INTERPOLATIONID, "string interpolator") - final val SYMBOLLIT = 11; enter(SYMBOLLIT, "symbol literal") // TODO: deprecate + //final val INTERPOLATIONID = 10; enter(INTERPOLATIONID, "string interpolator") + //final val SYMBOLLIT = 11; enter(SYMBOLLIT, "symbol literal") // TODO: deprecate /** identifiers */ final val IDENTIFIER = 12; enter(IDENTIFIER, "identifier") - final val BACKQUOTED_IDENT = 13; enter(BACKQUOTED_IDENT, "identifier", "backquoted ident") - - final val identifierTokens = BitSet(IDENTIFIER, BACKQUOTED_IDENT) - - def isIdentifier(token : Int) = - token >= IDENTIFIER && token <= BACKQUOTED_IDENT + //final val BACKQUOTED_IDENT = 13; enter(BACKQUOTED_IDENT, "identifier", "backquoted ident") /** alphabetic keywords */ final val IF = 20; enter(IF, "if") @@ -60,67 +55,63 @@ object Tokens { final val THIS = 23; enter(THIS, "this") final val NULL = 24; enter(NULL, "null") final val NEW = 25; enter(NEW, "new") - final val WITH = 26; enter(WITH, "with") + //final val WITH = 26; enter(WITH, "with") final val SUPER = 27; enter(SUPER, "super") - final val CASE = 28; enter(CASE, "case") - final val CASECLASS = 29; enter(CASECLASS, "case class") - final val CASEOBJECT = 30; enter(CASEOBJECT, "case object") - final val VAL = 31; enter(VAL, "val") + //final val CASE = 28; enter(CASE, "case") + //final val CASECLASS = 29; enter(CASECLASS, "case class") + //final val CASEOBJECT = 30; enter(CASEOBJECT, "case object") + //final val VAL = 31; enter(VAL, "val") final val ABSTRACT = 32; enter(ABSTRACT, "abstract") final val FINAL = 33; enter(FINAL, "final") final val PRIVATE = 34; enter(PRIVATE, "private") final val PROTECTED = 35; enter(PROTECTED, "protected") final val OVERRIDE = 36; enter(OVERRIDE, "override") - final val IMPLICIT = 37; enter(IMPLICIT, "implicit") - final val VAR = 38; enter(VAR, "var") - final val DEF = 39; enter(DEF, "def") - final val TYPE = 40; enter(TYPE, "type") + //final val IMPLICIT = 37; enter(IMPLICIT, "implicit") + //final val VAR = 38; enter(VAR, "var") + //final val DEF = 39; enter(DEF, "def") + //final val TYPE = 40; enter(TYPE, "type") final val EXTENDS = 41; enter(EXTENDS, "extends") final val TRUE = 42; enter(TRUE, "true") final val FALSE = 43; enter(FALSE, "false") - final val OBJECT = 44; enter(OBJECT, "object") + //final val OBJECT = 44; enter(OBJECT, "object") final val CLASS = 45; enter(CLASS, "class") final val IMPORT = 46; enter(IMPORT, "import") final val PACKAGE = 47; enter(PACKAGE, "package") - final val YIELD = 48; enter(YIELD, "yield") + //final val YIELD = 48; enter(YIELD, "yield") final val DO = 49; enter(DO, "do") - final val TRAIT = 50; enter(TRAIT, "trait") - final val SEALED = 51; enter(SEALED, "sealed") + //final val TRAIT = 50; enter(TRAIT, "trait") + //final val SEALED = 51; enter(SEALED, "sealed") final val THROW = 52; enter(THROW, "throw") final val TRY = 53; enter(TRY, "try") final val CATCH = 54; enter(CATCH, "catch") final val FINALLY = 55; enter(FINALLY, "finally") final val WHILE = 56; enter(WHILE, "while") final val RETURN = 57; enter(RETURN, "return") - final val MATCH = 58; enter(MATCH, "match") - final val LAZY = 59; enter(LAZY, "lazy") - final val THEN = 60; enter(THEN, "then") - final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate - - final val alphaKeywords = tokenRange(IF, FORSOME) + //final val MATCH = 58; enter(MATCH, "match") + //final val LAZY = 59; enter(LAZY, "lazy") + //final val THEN = 60; enter(THEN, "then") + //final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate /** special symbols */ final val COMMA = 70; enter(COMMA, "','") final val SEMI = 71; enter(DOT, "'.'") final val DOT = 72; enter(SEMI, "';'") - final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") - final val NEWLINES = 79; enter(NEWLINES, "end of statement", "new lines") + //final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") + //final val NEWLINES = 79; enter(NEWLINES, "end of statement", "new lines") /** special keywords */ - final val USCORE = 73; enter(USCORE, "_") + //final val USCORE = 73; enter(USCORE, "_") final val COLON = 74; enter(COLON, ":") final val EQUALS = 75; enter(EQUALS, "=") - final val LARROW = 76; enter(LARROW, "<-") - final val ARROW = 77; enter(ARROW, "=>") - final val SUBTYPE = 80; enter(SUBTYPE, "<:") - final val SUPERTYPE = 81; enter(SUPERTYPE, ">:") - final val HASH = 82; enter(HASH, "#") + //final val LARROW = 76; enter(LARROW, "<-") + //final val ARROW = 77; enter(ARROW, "=>") + //final val SUBTYPE = 80; enter(SUBTYPE, "<:") + //final val SUPERTYPE = 81; enter(SUPERTYPE, ">:") + //final val HASH = 82; enter(HASH, "#") final val AT = 83; enter(AT, "@") - final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate + //final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate - final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND) - final val symbolicTokens = tokenRange(COMMA, VIEWBOUND) - final val keywords = alphaKeywords | symbolicKeywords + val keywords: TokenSet /** parentheses */ final val LPAREN = 90; enter(LPAREN, "'('") @@ -133,9 +124,75 @@ object Tokens { final val firstParen = LPAREN final val lastParen = RBRACE + def buildKeywordArray(keywords: TokenSet) = { + def start(tok: Token) = tokenString(tok).toTermName.start + def sourceKeywords = keywords.toList.filter { (kw: Token) => + val ts = tokenString(kw) + (ts != null) && !ts.contains(' ') + } + + val lastKeywordStart = sourceKeywords.map(start).max + + val arr = Array.fill(lastKeywordStart + 1)(IDENTIFIER) + for (kw <- sourceKeywords) arr(start(kw)) = kw + (lastKeywordStart, arr) + } +} + +object Tokens extends TokensCommon { + final val minToken = EMPTY + final val maxToken = XMLSTART + + final val INTERPOLATIONID = 10; enter(INTERPOLATIONID, "string interpolator") + final val SYMBOLLIT = 11; enter(SYMBOLLIT, "symbol literal") // TODO: deprecate + + final val BACKQUOTED_IDENT = 13; enter(BACKQUOTED_IDENT, "identifier", "backquoted ident") + + final val identifierTokens = BitSet(IDENTIFIER, BACKQUOTED_IDENT) + + def isIdentifier(token : Int) = + token >= IDENTIFIER && token <= BACKQUOTED_IDENT + + /** alphabetic keywords */ + final val WITH = 26; enter(WITH, "with") + final val CASE = 28; enter(CASE, "case") + final val CASECLASS = 29; enter(CASECLASS, "case class") + final val CASEOBJECT = 30; enter(CASEOBJECT, "case object") + final val VAL = 31; enter(VAL, "val") + final val IMPLICIT = 37; enter(IMPLICIT, "implicit") + final val VAR = 38; enter(VAR, "var") + final val DEF = 39; enter(DEF, "def") + final val TYPE = 40; enter(TYPE, "type") + final val OBJECT = 44; enter(OBJECT, "object") + final val YIELD = 48; enter(YIELD, "yield") + final val TRAIT = 50; enter(TRAIT, "trait") + final val SEALED = 51; enter(SEALED, "sealed") + final val MATCH = 58; enter(MATCH, "match") + final val LAZY = 59; enter(LAZY, "lazy") + final val THEN = 60; enter(THEN, "then") + final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate + + /** special symbols */ + final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") + final val NEWLINES = 79; enter(NEWLINES, "end of statement", "new lines") + + /** special keywords */ + final val USCORE = 73; enter(USCORE, "_") + final val LARROW = 76; enter(LARROW, "<-") + final val ARROW = 77; enter(ARROW, "=>") + final val SUBTYPE = 80; enter(SUBTYPE, "<:") + final val SUPERTYPE = 81; enter(SUPERTYPE, ">:") + final val HASH = 82; enter(HASH, "#") + final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate + /** XML mode */ final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate + final val alphaKeywords = tokenRange(IF, FORSOME) + final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND) + final val symbolicTokens = tokenRange(COMMA, VIEWBOUND) + final val keywords = alphaKeywords | symbolicKeywords + final val allTokens = tokenRange(minToken, maxToken) final val literalTokens = tokenRange(CHARLIT, SYMBOLLIT) | BitSet(TRUE, FALSE, NULL) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index f3e9fda7443b..d3cc4a47dd9d 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -179,7 +179,7 @@ trait Checking { def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = { if (!proto.isInstanceOf[SelectionProto]) { val sym = tree.tpe.termSymbol - if ((sym is Package) || (sym is JavaModule)) ctx.error(d"$sym is not a value", tree.pos) + if ((sym is Package) || ((sym is JavaModule) && !ctx.compilationUnit.isJava)) ctx.error(d"$sym is not a value", tree.pos) } tree } diff --git a/src/dotty/tools/dotc/typer/FrontEnd.scala b/src/dotty/tools/dotc/typer/FrontEnd.scala index 4c5e2549bf1c..496c3e987c50 100644 --- a/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -4,6 +4,7 @@ package typer import core._ import Phases._ import Contexts._ +import dotty.tools.dotc.parsing.JavaParsers.JavaParser import parsing.Parsers.Parser import config.Printers._ import util.Stats._ @@ -22,7 +23,9 @@ class FrontEnd extends Phase { def parse(implicit ctx: Context) = monitor("parsing") { val unit = ctx.compilationUnit - unit.untpdTree = new Parser(unit.source).parse() + unit.untpdTree = + if(unit.isJava) new JavaParser(unit.source).parse() + else new Parser(unit.source).parse() typr.println("parsed:\n"+unit.untpdTree.show) } From 8c7f8ae7e0ec0779ff42d11046ec3a4ed8ddf9aa Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Wed, 8 Oct 2014 11:49:19 +0200 Subject: [PATCH 02/15] desugar Java repeated parms into an Array instead of Seq --- src/dotty/tools/dotc/TypeErasure.scala | 3 ++- src/dotty/tools/dotc/ast/Desugar.scala | 7 ++++--- src/dotty/tools/dotc/core/TypeApplications.scala | 5 ++++- src/dotty/tools/dotc/core/Types.scala | 4 +++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 851be76583bf..5ac6084b5444 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -94,8 +94,9 @@ object TypeErasure { def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp)(erasureCtx) def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)(erasureCtx) def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = { + val seqClass = if(isJava) defn.ArrayClass else defn.SeqClass val normTp = - if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass, defn.SeqClass) + if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass, seqClass) else tp (if (isJava) javaSigFn else scalaSigFn).sigName(normTp)(erasureCtx) } diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index dba3872cb127..1df4ef51d08d 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -768,11 +768,12 @@ object desugar { else // l.op(r), or val x = r; l.op(x), plus handle named args specially makeBinop(l, op, r) case PostfixOp(t, op) => - if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) + if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) { + val seqClass = if (ctx.compilationUnit.isJava) defn.ArrayClass else defn.SeqClass Annotated( New(ref(defn.RepeatedAnnot.typeRef), Nil :: Nil), - AppliedTypeTree(ref(defn.SeqClass.typeRef), t)) - else { + AppliedTypeTree(ref(seqClass.typeRef), t)) + } else { assert(ctx.mode.isExpr, ctx.mode) Select(t, op) } diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index bf756facfa28..3beb680d9c92 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -284,7 +284,10 @@ class TypeApplications(val self: Type) extends AnyVal { * or, if isJava is true, Array type, else the type itself. */ def underlyingIfRepeated(isJava: Boolean)(implicit ctx: Context): Type = - if (self.isRepeatedParam) translateParameterized(defn.RepeatedParamClass, defn.SeqClass) + if (self.isRepeatedParam) { + val seqClass = if(isJava) defn.ArrayClass else defn.SeqClass + translateParameterized(defn.RepeatedParamClass, seqClass) + } else self /** If this is an encoding of a (partially) applied type, return its arguments, diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d0ddfdd28b50..d3aacefac69d 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1931,7 +1931,9 @@ object Types { def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { def paramInfo(param: Symbol): Type = param.info match { case AnnotatedType(annot, tp) if annot matches defn.RepeatedAnnot => - tp.translateParameterized(defn.SeqClass, defn.RepeatedParamClass) + val typeSym = param.info.typeSymbol.asClass + assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass) + tp.translateParameterized(typeSym, defn.RepeatedParamClass) case tp => tp } From a1d031fa5903bb47f5a2637a83ffda8e27e1688d Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Wed, 8 Oct 2014 16:21:31 +0200 Subject: [PATCH 03/15] Java Select: try typing as both SelectFromTypeTree and Select --- src/dotty/tools/dotc/typer/Typer.scala | 41 ++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 1afa5f9f33f9..e14be98bd34d 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -168,9 +168,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit selectors match { case Pair(Ident(from), Ident(Name)) :: rest => val selName = if (name.isTypeName) from.toTypeName else from - checkUnambiguous(selectionType(site, selName, tree.pos)) + checkUnambiguous(selectionType(site, selName, tree.pos)(refctx)) case Ident(Name) :: rest => - checkUnambiguous(selectionType(site, name, tree.pos)) + checkUnambiguous(selectionType(site, name, tree.pos)(refctx)) case _ :: rest => namedImportRef(site, rest) case nil => @@ -278,9 +278,40 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { - val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) - if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) - checkValue(assignType(cpy.Select(tree)(qual1, tree.name), qual1), pt) + def asSelect(implicit ctx: Context): Tree = { + val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) + if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) + checkValue(assignType(cpy.Select(tree)(qual1, tree.name), qual1), pt) + } + + def asJavaSelectFromTypeTree(implicit ctx: Context): Tree = { + // Translate names in Select/Ident nodes to type names. + def convertToTypeName(tree: untpd.Tree): Option[untpd.Tree] = tree match { + case Select(qual, name) => Some(untpd.Select(qual, name.toTypeName)) + case Ident(name) => Some(untpd.Ident(name.toTypeName)) + case _ => None + } + + // Try to convert Select(qual, name) to a SelectFromTypeTree. + def convertToSelectFromType(qual: untpd.Tree, origName: Name): Option[untpd.SelectFromTypeTree] = + convertToTypeName(qual) match { + case Some(qual1) => Some(untpd.SelectFromTypeTree(qual1 withPos qual.pos, origName.toTypeName)) + case _ => None + } + + convertToSelectFromType(tree.qualifier, tree.name) match { + case Some(sftt) => + println(s"$tree converted to $sftt") + typedSelectFromTypeTree(sftt, pt) + case _ => ctx.error(d"Could not convert $tree to a SelectFromTypeTree"); EmptyTree + } + } + + if(ctx.compilationUnit.isJava && tree.name.isTypeName) { + // SI-3120 Java uses the same syntax, A.B, to express selection from the + // value A and from the type A. We have to try both. + tryEither(tryCtx => asSelect(tryCtx))((_,_) => asJavaSelectFromTypeTree(ctx)) + } else asSelect(ctx) } def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): Tree = track("typedSelectFromTypeTree") { From 2c39a7c9854e194fda3ab8a2251b480beb667337 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Wed, 8 Oct 2014 16:55:49 +0200 Subject: [PATCH 04/15] support running java-interop tests --- test/dotc/tests.scala | 7 +++++++ test/test/CompilerTest.scala | 6 +++--- .../java-interop/{pos => failing}/t1751/A1_2.scala | 0 .../java-interop/{pos => failing}/t1751/A2_1.scala | 0 .../java-interop/{pos => failing}/t1751/SuiteClasses.java | 0 tests/disabled/java-interop/{pos => failing}/t2409/J.java | 0 .../java-interop/{pos => failing}/t2409/t2409.scala | 0 tests/disabled/java-interop/{pos => failing}/t294/Ann.java | 0 .../disabled/java-interop/{pos => failing}/t294/Ann2.java | 0 .../java-interop/{pos => failing}/t294/Test_1.scala | 0 .../java-interop/{pos => failing}/t294/Test_2.scala | 0 tests/disabled/java-interop/pos/t1782/Test_1.scala | 2 +- 12 files changed, 11 insertions(+), 4 deletions(-) rename tests/disabled/java-interop/{pos => failing}/t1751/A1_2.scala (100%) rename tests/disabled/java-interop/{pos => failing}/t1751/A2_1.scala (100%) rename tests/disabled/java-interop/{pos => failing}/t1751/SuiteClasses.java (100%) rename tests/disabled/java-interop/{pos => failing}/t2409/J.java (100%) rename tests/disabled/java-interop/{pos => failing}/t2409/t2409.scala (100%) rename tests/disabled/java-interop/{pos => failing}/t294/Ann.java (100%) rename tests/disabled/java-interop/{pos => failing}/t294/Ann2.java (100%) rename tests/disabled/java-interop/{pos => failing}/t294/Test_1.scala (100%) rename tests/disabled/java-interop/{pos => failing}/t294/Test_2.scala (100%) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 4c6d004bf216..b4a84c086539 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -132,5 +132,12 @@ class tests extends CompilerTest { "-Xprompt", "#runs", "2")) + val javaDir = "./tests/disabled/java-interop/" + @Test def java_all = compileFiles(javaDir+"pos/") + + @Test def java_2409 = compileDir(javaDir+"failing/t2409") + @Test def java_1751 = compileDir(javaDir+"failing/t1751") + @Test def java_294 = compileDir(javaDir+"failing/t294") + //@Test def dotc_compilercommand = compileFile(dotcDir + "tools/dotc/config/", "CompilerCommand") } diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala index c9c7c602bdea..ccee7467f0b5 100644 --- a/test/test/CompilerTest.scala +++ b/test/test/CompilerTest.scala @@ -28,13 +28,13 @@ class CompilerTest extends DottyTest { case "-deep" :: args1 => (dir.deepFiles, args1) case _ => (dir.files, args) } - val fileNames = files.toArray.map(_.toString).filter(_ endsWith ".scala") + val fileNames = files.toArray.map(_.toString).filter(name => (name endsWith ".scala") || (name endsWith ".java")) compileArgs(fileNames ++ normArgs, xerrors) } def compileFiles(path: String, args: List[String] = Nil)(implicit defaultOptions: List[String]): Unit = { val dir = Directory(path) - val fileNames = dir.files.toArray.map(_.toString).filter(_ endsWith ".scala") + val fileNames = dir.files.toArray.map(_.toString).filter(name => (name endsWith ".scala") || (name endsWith ".java")) for (name <- fileNames) { println(s"testing $name") compileArgs((name :: args).toArray, 0) @@ -57,4 +57,4 @@ object CompilerTest extends App { // new CompilerTest().compileDir(dotcDir + "tools/dotc") // new CompilerTest().compileFile(dotcDir + "tools/dotc/", "Run") -} \ No newline at end of file +} diff --git a/tests/disabled/java-interop/pos/t1751/A1_2.scala b/tests/disabled/java-interop/failing/t1751/A1_2.scala similarity index 100% rename from tests/disabled/java-interop/pos/t1751/A1_2.scala rename to tests/disabled/java-interop/failing/t1751/A1_2.scala diff --git a/tests/disabled/java-interop/pos/t1751/A2_1.scala b/tests/disabled/java-interop/failing/t1751/A2_1.scala similarity index 100% rename from tests/disabled/java-interop/pos/t1751/A2_1.scala rename to tests/disabled/java-interop/failing/t1751/A2_1.scala diff --git a/tests/disabled/java-interop/pos/t1751/SuiteClasses.java b/tests/disabled/java-interop/failing/t1751/SuiteClasses.java similarity index 100% rename from tests/disabled/java-interop/pos/t1751/SuiteClasses.java rename to tests/disabled/java-interop/failing/t1751/SuiteClasses.java diff --git a/tests/disabled/java-interop/pos/t2409/J.java b/tests/disabled/java-interop/failing/t2409/J.java similarity index 100% rename from tests/disabled/java-interop/pos/t2409/J.java rename to tests/disabled/java-interop/failing/t2409/J.java diff --git a/tests/disabled/java-interop/pos/t2409/t2409.scala b/tests/disabled/java-interop/failing/t2409/t2409.scala similarity index 100% rename from tests/disabled/java-interop/pos/t2409/t2409.scala rename to tests/disabled/java-interop/failing/t2409/t2409.scala diff --git a/tests/disabled/java-interop/pos/t294/Ann.java b/tests/disabled/java-interop/failing/t294/Ann.java similarity index 100% rename from tests/disabled/java-interop/pos/t294/Ann.java rename to tests/disabled/java-interop/failing/t294/Ann.java diff --git a/tests/disabled/java-interop/pos/t294/Ann2.java b/tests/disabled/java-interop/failing/t294/Ann2.java similarity index 100% rename from tests/disabled/java-interop/pos/t294/Ann2.java rename to tests/disabled/java-interop/failing/t294/Ann2.java diff --git a/tests/disabled/java-interop/pos/t294/Test_1.scala b/tests/disabled/java-interop/failing/t294/Test_1.scala similarity index 100% rename from tests/disabled/java-interop/pos/t294/Test_1.scala rename to tests/disabled/java-interop/failing/t294/Test_1.scala diff --git a/tests/disabled/java-interop/pos/t294/Test_2.scala b/tests/disabled/java-interop/failing/t294/Test_2.scala similarity index 100% rename from tests/disabled/java-interop/pos/t294/Test_2.scala rename to tests/disabled/java-interop/failing/t294/Test_2.scala diff --git a/tests/disabled/java-interop/pos/t1782/Test_1.scala b/tests/disabled/java-interop/pos/t1782/Test_1.scala index 6467a74c2974..47495c082929 100644 --- a/tests/disabled/java-interop/pos/t1782/Test_1.scala +++ b/tests/disabled/java-interop/pos/t1782/Test_1.scala @@ -1,6 +1,6 @@ @ImplementedBy(classOf[Provider]) trait Service { - def someMethod() + def someMethod(): Unit } class Provider From 861b94b3b5eb89a4c37c26f1492629717b6f78a3 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Thu, 9 Oct 2014 22:36:30 +0200 Subject: [PATCH 05/15] make elimrepeated addVarArgsBridge at thisTransformer instead of thisTransformer.next --- src/dotty/tools/dotc/transform/ElimRepeated.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala index eb44c3e2cdc6..2e50fcd08c41 100644 --- a/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -63,7 +63,7 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer { thisTransfo assert(ctx.phase == thisTransformer) def overridesJava = tree.symbol.allOverriddenSymbols.exists(_ is JavaDefined) if (tree.symbol.info.isVarArgsMethod && overridesJava) - addVarArgsBridge(tree)(ctx.withPhase(thisTransformer.next)) + addVarArgsBridge(tree)(ctx.withPhase(thisTransformer)) else tree } From c9fbc9224a8152a465f2693ad6d2da4e739bd11e Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Fri, 10 Oct 2014 13:44:59 +0200 Subject: [PATCH 06/15] add mapping ENUM -> Enum to PickleBuffer --- src/dotty/tools/dotc/core/pickling/PickleBuffer.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala index d2a05bf3a4dc..c15d1ee0cc67 100644 --- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala @@ -251,7 +251,8 @@ object PickleBuffer { SPECIALIZED -> Specialized, DEFAULTINIT -> DefaultInit, VBRIDGE -> VBridge, - VARARGS -> JavaVarargs) + VARARGS -> JavaVarargs, + ENUM -> Enum) // generate initial maps from Scala flags to Dotty flags val termMap, typeMap = new Array[Long](64) From 8e2c11f414254c3e664ef28380bf81eccaed5c78 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Fri, 10 Oct 2014 13:48:21 +0200 Subject: [PATCH 07/15] add comment explaining why checkValue skips Java compilation units --- src/dotty/tools/dotc/typer/Checking.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index d3cc4a47dd9d..e0a7c557d059 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -179,6 +179,8 @@ trait Checking { def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = { if (!proto.isInstanceOf[SelectionProto]) { val sym = tree.tpe.termSymbol + // The check is avoided inside Java compilation units because it always fails + // on the singleton type Module.type. if ((sym is Package) || ((sym is JavaModule) && !ctx.compilationUnit.isJava)) ctx.error(d"$sym is not a value", tree.pos) } tree From 9164e1f4af64e60a8562e10aa231aaea85cbd9a9 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Fri, 10 Oct 2014 13:50:15 +0200 Subject: [PATCH 08/15] add comment to explain why refctx passed explicitly to selectionType --- src/dotty/tools/dotc/typer/Typer.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index e14be98bd34d..30516948939a 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -168,6 +168,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit selectors match { case Pair(Ident(from), Ident(Name)) :: rest => val selName = if (name.isTypeName) from.toTypeName else from + // Pass refctx so that any errors are reported in the context of the + // reference instead of the context of the import. checkUnambiguous(selectionType(site, selName, tree.pos)(refctx)) case Ident(Name) :: rest => checkUnambiguous(selectionType(site, name, tree.pos)(refctx)) From 18cfc597fe9da8c45bc7cf6a4d2404213ee0c873 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Fri, 10 Oct 2014 13:51:25 +0200 Subject: [PATCH 09/15] remove debugging println --- src/dotty/tools/dotc/typer/Typer.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 30516948939a..cd5fa9e8b8e9 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -302,9 +302,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } convertToSelectFromType(tree.qualifier, tree.name) match { - case Some(sftt) => - println(s"$tree converted to $sftt") - typedSelectFromTypeTree(sftt, pt) + case Some(sftt) => typedSelectFromTypeTree(sftt, pt) case _ => ctx.error(d"Could not convert $tree to a SelectFromTypeTree"); EmptyTree } } From bdd5887ddd51383022f54c234695799112c986fa Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Wed, 29 Oct 2014 17:10:17 +0100 Subject: [PATCH 10/15] Flags.Static renamed to JavaStatic --- src/dotty/tools/dotc/parsing/JavaParsers.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala index 095342c48291..81e2bb50260d 100644 --- a/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -369,7 +369,7 @@ object JavaParsers { flags |= Flags.Private in.nextToken() case STATIC => - flags |= Flags.Static + flags |= Flags.JavaStatic in.nextToken() case ABSTRACT => flags |= Flags.Abstract @@ -524,7 +524,7 @@ object JavaParsers { } } } else { - if (inInterface) mods1 |= Flags.Final | Flags.Static + if (inInterface) mods1 |= Flags.Final | Flags.JavaStatic val result = fieldDecls(Position(offset), mods1, rtpt, name) accept(SEMI) result @@ -582,7 +582,7 @@ object JavaParsers { def memberDecl(mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match { case CLASS | ENUM | INTERFACE | AT => - typeDecl(if (definesInterface(parentToken)) mods | Flags.Static else mods) + typeDecl(if (definesInterface(parentToken)) mods | Flags.JavaStatic else mods) case _ => termDecl(mods, parentToken, parentTParams) } @@ -732,9 +732,9 @@ object JavaParsers { } else if (in.token == SEMI) { in.nextToken() } else { - if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.Static + if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.JavaStatic val decls = memberDecl(mods, parentToken, parentTParams) - (if ((mods is Flags.Static) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) + (if ((mods is Flags.JavaStatic) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) statics else members) ++= decls @@ -802,12 +802,12 @@ object JavaParsers { } val predefs = List( DefDef( - Modifiers(Flags.JavaDefined | Flags.Static | Flags.Method), nme.values, List(), + Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method), nme.values, List(), ListOfNil, arrayOf(enumType), unimplementedExpr), DefDef( - Modifiers(Flags.JavaDefined | Flags.Static | Flags.Method), nme.valueOf, List(), + Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method), nme.valueOf, List(), List(List(makeParam("x", TypeTree(StringType)))), enumType, unimplementedExpr)) @@ -839,7 +839,7 @@ object JavaParsers { skipAhead() accept(RBRACE) } - ValDef(Modifiers(Flags.Enum | Flags.Stable | Flags.JavaDefined | Flags.Static), name.toTermName, enumType, unimplementedExpr) + ValDef(Modifiers(Flags.Enum | Flags.Stable | Flags.JavaDefined | Flags.JavaStatic), name.toTermName, enumType, unimplementedExpr) } } From 77d2d0cd631d328e9217f462c6b94f032593932c Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Wed, 29 Oct 2014 17:12:36 +0100 Subject: [PATCH 11/15] fix calls to tree copier that now requires multiple parameter lists --- src/dotty/tools/dotc/parsing/JavaParsers.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala index 81e2bb50260d..f524827b9538 100644 --- a/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -618,8 +618,8 @@ object JavaParsers { if (statics.isEmpty) cdef else { val template = cdef.rhs.asInstanceOf[Template] - cpy.TypeDef(cdef, cdef.mods, cdef.name, - cpy.Template(template, template.constr, template.parents, template.self, + cpy.TypeDef(cdef)(cdef.mods, cdef.name, + cpy.Template(template)(template.constr, template.parents, template.self, importCompanionObject(cdef) :: template.body), cdef.tparams) } From 9eb65e74ba70d779e603516ea1e15e106d33ae92 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Wed, 29 Oct 2014 17:28:02 +0100 Subject: [PATCH 12/15] make annotation classes abstract (since they are interfaces) --- src/dotty/tools/dotc/parsing/JavaParsers.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala index f524827b9538..d30a4ceb0114 100644 --- a/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -770,7 +770,7 @@ object JavaParsers { val body1 = body.filterNot(_.isInstanceOf[DefDef]) val templ = makeTemplate(annotationParents, constr :: body1, List()) addCompanionObject(statics, atPos(offset) { - TypeDef(mods, name, templ) + TypeDef(mods | Flags.Abstract, name, templ) }) } From 6fc23daea8188e1c2ff94352e7ef096a54dfd0c4 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Thu, 30 Oct 2014 17:08:22 +0100 Subject: [PATCH 13/15] For Java constructors, do not move out to ctx.outer A Java constructor needs to see the import of the companion object of the class. It is not necessary to move to an outer context because a Java constructor does not have an implementation. scalac also does it this way: see Namers.Namer.createNamer.isConstrParam. --- src/dotty/tools/dotc/typer/Namer.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 3a1f0a98b461..f936aeddab96 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -243,7 +243,10 @@ class Namer { typer: Typer => // different: The former must have the class as owner (because the // constructor is owned by the class), the latter must not (because // constructor parameters are interpreted as if they are outside the class). - val cctx = if (tree.name == nme.CONSTRUCTOR) ctx.outer else ctx + // Don't do this for Java constructors because they need to see the import + // of the companion object, and it is not necessary for them because they + // have no implementation. + val cctx = if (tree.name == nme.CONSTRUCTOR && !(tree.mods is JavaDefined)) ctx.outer else ctx record(ctx.newSymbol( ctx.owner, name, tree.mods.flags | deferred | method | higherKinded | inSuperCall1, From fa89a961017af433a741777e495c03797b2eb463 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Wed, 29 Oct 2014 17:29:40 +0100 Subject: [PATCH 14/15] create dummy first constructor for Java classes The dummy constructor is needed so that the real constructors see the import of the companion object. The constructor has a parameter of type Unit so that no Java code can call it. --- .../tools/dotc/parsing/JavaParsers.scala | 47 ++++++++++--------- test/dotc/tests.scala | 1 - .../{failing => pos}/t2409/J.java | 0 .../{failing => pos}/t2409/t2409.scala | 0 4 files changed, 26 insertions(+), 22 deletions(-) rename tests/disabled/java-interop/{failing => pos}/t2409/J.java (100%) rename tests/disabled/java-interop/{failing => pos}/t2409/t2409.scala (100%) diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala index d30a4ceb0114..36f1c8e18e6d 100644 --- a/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -112,7 +112,7 @@ object JavaParsers { def makePackaging(pkg: RefTree, stats: List[Tree]): PackageDef = atPos(pkg.pos) { PackageDef(pkg, stats) } - def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef]) = { + def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef], needsDummyConstr: Boolean) = { def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match { case (meth: DefDef) :: rest if meth.name.isConstructorName => (meth, rest) case first :: rest => @@ -120,21 +120,26 @@ object JavaParsers { (constr, first :: tail) case nil => (EmptyTree, nil) } - val (constr, stats1) = pullOutFirstConstr(stats) - val constr1 = if(constr == EmptyTree) makeConstructor(List(), tparams) else constr.asInstanceOf[DefDef] - Template(constr1, parents, EmptyValDef, stats1) + var (constr1, stats1) = pullOutFirstConstr(stats) + if(constr1 == EmptyTree) constr1 = makeConstructor(List(), tparams) + // A dummy first constructor is needed for Java classes so that the real constructors see the + // import of the companion object. The constructor has parameter of type Unit so no Java code + // can call it. + if(needsDummyConstr) { + stats1 = constr1 :: stats1 + constr1 = makeConstructor(List(scalaDot(tpnme.Unit)), tparams, Flags.JavaDefined | Flags.PrivateLocal) + } + Template(constr1.asInstanceOf[DefDef], parents, EmptyValDef, stats1) } def makeSyntheticParam(count: Int, tpt: Tree): ValDef = makeParam(nme.syntheticParamName(count), tpt) - def makeParam(name: String, tpt: Tree): ValDef = - makeParam(name.toTermName, tpt) - def makeParam(name: TermName, tpt: Tree, flags: FlagSet = Flags.Param): ValDef = - ValDef(Modifiers(flags | Flags.JavaDefined), name, tpt, EmptyTree) + def makeParam(name: TermName, tpt: Tree): ValDef = + ValDef(Modifiers(Flags.JavaDefined | Flags.PrivateLocalParamAccessor), name, tpt, EmptyTree) - def makeConstructor(formals: List[Tree], tparams: List[TypeDef]) = { + def makeConstructor(formals: List[Tree], tparams: List[TypeDef], flags: FlagSet = Flags.JavaDefined) = { val vparams = mapWithIndex(formals)((p, i) => makeSyntheticParam(i + 1, p)) - DefDef(Modifiers(Flags.JavaDefined), nme.CONSTRUCTOR, tparams, List(vparams), TypeTree(), EmptyTree) + DefDef(Modifiers(flags), nme.CONSTRUCTOR, tparams, List(vparams), TypeTree(), EmptyTree) } // ------------- general parsing --------------------------- @@ -447,7 +452,7 @@ object JavaParsers { PostfixOp(t, nme.raw.STAR) } } - varDecl(Position(in.offset), Modifiers(Flags.JavaDefined | Flags.Param), t, ident().toTermName, dontAddMutable = true) + varDecl(Position(in.offset), Modifiers(Flags.JavaDefined | Flags.Param), t, ident().toTermName) } def optThrows(): Unit = { @@ -571,12 +576,12 @@ object JavaParsers { buf.toList } - def varDecl(pos: Position, mods: Modifiers, tpt: Tree, name: TermName, dontAddMutable: Boolean = false): ValDef = { + def varDecl(pos: Position, mods: Modifiers, tpt: Tree, name: TermName): ValDef = { val tpt1 = optArrayBrackets(tpt) if (in.token == EQUALS && !(mods is Flags.Param)) skipTo(COMMA, SEMI) - val mods1 = if (mods is Flags.Final) mods &~ Flags.Final else if(dontAddMutable) mods else mods | Flags.Mutable + val mods1 = if(mods is Flags.Final) mods else mods | Flags.Mutable atPos(pos) { - ValDef(mods1, name, tpt1, EmptyTree) + ValDef(mods1, name, tpt1, if(mods is Flags.Param) EmptyTree else unimplementedExpr) } } @@ -590,7 +595,7 @@ object JavaParsers { def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree = atPos(cdef.pos) { ModuleDef((cdef.mods & (Flags.AccessFlags | Flags.JavaDefined)).toTermFlags, cdef.name.toTermName, - makeTemplate(List(), statics, List())) + makeTemplate(List(), statics, List(), false)) } private val wild = Ident(nme.WILDCARD) withPos Position(-1) @@ -689,7 +694,7 @@ object JavaParsers { val interfaces = interfacesOpt() val (statics, body) = typeBody(CLASS, name, tparams) addCompanionObject(statics, atPos(offset) { - TypeDef(mods, name, makeTemplate(superclass :: interfaces, body, tparams)) + TypeDef(mods, name, makeTemplate(superclass :: interfaces, body, tparams, true)) }) } @@ -709,7 +714,7 @@ object JavaParsers { addCompanionObject(statics, atPos(offset) { TypeDef(mods | Flags.Trait | Flags.Interface | Flags.Abstract, name, tparams, - makeTemplate(parents, body, tparams)) + makeTemplate(parents, body, tparams, false)) }) } @@ -763,12 +768,12 @@ object JavaParsers { val name = identForType() val (statics, body) = typeBody(AT, name, List()) val constructorParams = body.collect { - case dd: DefDef => makeParam(dd.name, dd.tpt, Flags.PrivateLocalParamAccessor) + case dd: DefDef => makeParam(dd.name, dd.tpt) } val constr = DefDef(Modifiers(Flags.JavaDefined), nme.CONSTRUCTOR, List(), List(constructorParams), TypeTree(), EmptyTree) val body1 = body.filterNot(_.isInstanceOf[DefDef]) - val templ = makeTemplate(annotationParents, constr :: body1, List()) + val templ = makeTemplate(annotationParents, constr :: body1, List(), false) addCompanionObject(statics, atPos(offset) { TypeDef(mods | Flags.Abstract, name, templ) }) @@ -808,7 +813,7 @@ object JavaParsers { unimplementedExpr), DefDef( Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method), nme.valueOf, List(), - List(List(makeParam("x", TypeTree(StringType)))), + List(List(makeParam("x".toTermName, TypeTree(StringType)))), enumType, unimplementedExpr)) accept(RBRACE) @@ -821,7 +826,7 @@ object JavaParsers { List(Literal(Constant(null)),Literal(Constant(0)))) addCompanionObject(consts ::: statics ::: predefs, atPos(offset) { TypeDef(mods | Flags.Enum, name, List(), - makeTemplate(superclazz :: interfaces, body, List())) + makeTemplate(superclazz :: interfaces, body, List(), true)) }) } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index b4a84c086539..03d80730b1ea 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -135,7 +135,6 @@ class tests extends CompilerTest { val javaDir = "./tests/disabled/java-interop/" @Test def java_all = compileFiles(javaDir+"pos/") - @Test def java_2409 = compileDir(javaDir+"failing/t2409") @Test def java_1751 = compileDir(javaDir+"failing/t1751") @Test def java_294 = compileDir(javaDir+"failing/t294") diff --git a/tests/disabled/java-interop/failing/t2409/J.java b/tests/disabled/java-interop/pos/t2409/J.java similarity index 100% rename from tests/disabled/java-interop/failing/t2409/J.java rename to tests/disabled/java-interop/pos/t2409/J.java diff --git a/tests/disabled/java-interop/failing/t2409/t2409.scala b/tests/disabled/java-interop/pos/t2409/t2409.scala similarity index 100% rename from tests/disabled/java-interop/failing/t2409/t2409.scala rename to tests/disabled/java-interop/pos/t2409/t2409.scala From 70eb0f73b11ea9b2be9454b36ff67b4ca652d6d0 Mon Sep 17 00:00:00 2001 From: Ondrej Lhotak Date: Thu, 30 Oct 2014 21:46:16 +0100 Subject: [PATCH 15/15] don't try to make a body for a setter of a Java field transformSym explicitly checks that a field is JavaDefined and does not create a symbol for it. Creation of a setter body looks for the symbol and fails because it does not find it. We do not need setter bodies for Java fields because we are not generating bytecode for them. --- src/dotty/tools/dotc/transform/GettersSetters.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala index 772a63e52a6c..d8da2f22d9c4 100644 --- a/src/dotty/tools/dotc/transform/GettersSetters.scala +++ b/src/dotty/tools/dotc/transform/GettersSetters.scala @@ -104,7 +104,7 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf } override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = - if (tree.symbol.isSetter && !tree.symbol.is(Deferred | ParamAccessor)) { + if (tree.symbol.isSetter && !tree.symbol.is(Deferred | ParamAccessor | JavaDefined)) { val Literal(Constant(())) = tree.rhs val initializer = Assign(ref(tree.symbol.field), ref(tree.vparamss.head.head.symbol)) assert(initializer.hasType)