diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 27e97a92b48e..c54a60f5a8fa 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -204,6 +204,7 @@ object StdNames { final val Null: N = "Null" final val Object: N = "Object" final val FromJavaObject: N = "" + final val Record: N = "Record" final val Product: N = "Product" final val PartialFunction: N = "PartialFunction" final val PrefixType: N = "PrefixType" @@ -912,6 +913,10 @@ object StdNames { final val VOLATILEkw: N = kw("volatile") final val WHILEkw: N = kw("while") + final val RECORDid: N = "record" + final val VARid: N = "var" + final val YIELDid: N = "yield" + final val BoxedBoolean: N = "java.lang.Boolean" final val BoxedByte: N = "java.lang.Byte" final val BoxedCharacter: N = "java.lang.Character" @@ -944,6 +949,8 @@ object StdNames { final val JavaSerializable: N = "java.io.Serializable" } + + class JavaTermNames extends JavaNames[TermName] { protected def fromString(s: String): TermName = termName(s) } diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index daeebcbcc17c..02be373253ed 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -98,6 +98,8 @@ object JavaParsers { def javaLangObject(): Tree = javaLangDot(tpnme.Object) + def javaLangRecord(): Tree = javaLangDot(tpnme.Record) + def arrayOf(tpt: Tree): AppliedTypeTree = AppliedTypeTree(scalaDot(tpnme.Array), List(tpt)) @@ -555,6 +557,10 @@ object JavaParsers { def definesInterface(token: Int): Boolean = token == INTERFACE || token == AT + def adaptRecordIdentifier(): Unit = + if in.token == IDENTIFIER && in.name == jnme.RECORDid then + in.token = RECORD + def termDecl(start: Offset, 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() @@ -581,6 +587,16 @@ object JavaParsers { TypeTree(), methodBody()).withMods(mods) } } + } else if (in.token == LBRACE && rtptName != nme.EMPTY && parentToken == RECORD) { + /* + record RecordName(T param1, ...) { + RecordName { // <- here + // methodBody + } + } + */ + methodBody() + Nil } else { var mods1 = mods @@ -717,12 +733,11 @@ object JavaParsers { ValDef(name, tpt2, if (mods.is(Flags.Param)) EmptyTree else unimplementedExpr).withMods(mods1) } - def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match { - case CLASS | ENUM | INTERFACE | AT => + def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match + case CLASS | ENUM | RECORD | INTERFACE | AT => typeDecl(start, if (definesInterface(parentToken)) mods | Flags.JavaStatic else mods) case _ => termDecl(start, mods, parentToken, parentTParams) - } def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree = atSpan(cdef.span) { @@ -804,6 +819,48 @@ object JavaParsers { addCompanionObject(statics, cls) } + def recordDecl(start: Offset, mods: Modifiers): List[Tree] = + accept(RECORD) + val nameOffset = in.offset + val name = identForType() + val tparams = typeParams() + val header = formalParams() + val superclass = javaLangRecord() // records always extend java.lang.Record + val interfaces = interfacesOpt() // records may implement interfaces + val (statics, body) = typeBody(RECORD, name, tparams) + + // We need to generate accessors for every param, if no method with the same name is already defined + + var fieldsByName = header.map(v => (v.name, (v.tpt, v.mods.annotations))).toMap + + for case DefDef(name, paramss, tpt, rhs) <- body + if paramss.isEmpty && fieldsByName.contains(name) + do + fieldsByName -= name + end for + + val accessors = + (for (name, (tpt, annots)) <- fieldsByName yield + DefDef(name, Nil, tpt, unimplementedExpr).withMods(Modifiers(Flags.JavaDefined | Flags.Method | Flags.Synthetic)) + ).toList + + // generate the canonical constructor + val canonicalConstructor = makeConstructor(header, tparams) + + // return the trees, probably with addCompanionObject (like classDecl) + val recordTypeDef = atSpan(start, nameOffset) { + TypeDef(name, + makeTemplate( + parents = superclass :: interfaces, + stats = canonicalConstructor :: accessors ::: body, + tparams = tparams, + false + ) + ) + } + addCompanionObject(statics, recordTypeDef) + end recordDecl + def interfaceDecl(start: Offset, mods: Modifiers): List[Tree] = { accept(INTERFACE) val nameOffset = in.offset @@ -846,7 +903,7 @@ object JavaParsers { else if (in.token == SEMI) in.nextToken() else { - if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.JavaStatic + if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) mods |= Flags.JavaStatic val decls = memberDecl(start, mods, parentToken, parentTParams) (if (mods.is(Flags.JavaStatic) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) statics @@ -952,6 +1009,7 @@ object JavaParsers { case INTERFACE => interfaceDecl(start, mods) case AT => annotationDecl(start, mods) case CLASS => classDecl(start, mods) + case RECORD => recordDecl(start, mods) case _ => in.nextToken(); syntaxError(em"illegal start of type declaration", skipIt = true); List(errorTypeTree) } diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala b/compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala index 3e73b6d95adb..0781553d6a60 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala @@ -22,6 +22,7 @@ object JavaTokens extends TokensCommon { inline val INTERFACE = 105; enter(INTERFACE, "interface") inline val ENUM = 106; enter(ENUM, "enum") inline val IMPLEMENTS = 107; enter(IMPLEMENTS, "implements") + inline val RECORD = 108; enter(RECORD, "record") /** modifiers */ inline val PUBLIC = 110; enter(PUBLIC, "public")