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/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/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 } 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) diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala new file mode 100644 index 000000000000..36f1c8e18e6d --- /dev/null +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -0,0 +1,892 @@ +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], 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 => + val (constr, tail) = pullOutFirstConstr(rest) + (constr, first :: tail) + case nil => (EmptyTree, nil) + } + 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: TermName, tpt: Tree): ValDef = + ValDef(Modifiers(Flags.JavaDefined | Flags.PrivateLocalParamAccessor), name, tpt, EmptyTree) + + 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), 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.JavaStatic + 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) + } + + 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.JavaStatic + 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): ValDef = { + val tpt1 = optArrayBrackets(tpt) + if (in.token == EQUALS && !(mods is Flags.Param)) skipTo(COMMA, SEMI) + val mods1 = if(mods is Flags.Final) mods else mods | Flags.Mutable + atPos(pos) { + ValDef(mods1, name, tpt1, if(mods is Flags.Param) EmptyTree else unimplementedExpr) + } + } + + 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.JavaStatic 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(), false)) + } + + 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, true)) + }) + } + + 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, false)) + }) + } + + 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.JavaStatic + val decls = memberDecl(mods, parentToken, parentTParams) + (if ((mods is Flags.JavaStatic) || 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) + } + 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(), false) + addCompanionObject(statics, atPos(offset) { + TypeDef(mods | Flags.Abstract, 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.JavaStatic | Flags.Method), nme.values, List(), + ListOfNil, + arrayOf(enumType), + unimplementedExpr), + DefDef( + Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method), nme.valueOf, List(), + List(List(makeParam("x".toTermName, 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(), true)) + }) + } + + 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.JavaStatic), 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/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 } 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) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index f3e9fda7443b..e0a7c557d059 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -179,7 +179,9 @@ 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) + // 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 } 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) } 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, diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 1afa5f9f33f9..cd5fa9e8b8e9 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -168,9 +168,11 @@ 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)) + // 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)) + checkUnambiguous(selectionType(site, name, tree.pos)(refctx)) case _ :: rest => namedImportRef(site, rest) case nil => @@ -278,9 +280,38 @@ 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) => 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") { diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 4c6d004bf216..03d80730b1ea 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -132,5 +132,11 @@ class tests extends CompilerTest { "-Xprompt", "#runs", "2")) + val javaDir = "./tests/disabled/java-interop/" + @Test def java_all = compileFiles(javaDir+"pos/") + + @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/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