@@ -20,7 +20,9 @@ import StdNames._
2020import reporting ._
2121import dotty .tools .dotc .util .SourceFile
2222import util .Spans ._
23+
2324import scala .collection .mutable .ListBuffer
25+ import scala .collection .immutable .ListMap
2426
2527object JavaParsers {
2628
@@ -96,8 +98,12 @@ object JavaParsers {
9698 def javaLangDot (name : Name ): Tree =
9799 Select (javaDot(nme.lang), name)
98100
101+ /** Tree representing `java.lang.Object` */
99102 def javaLangObject (): Tree = javaLangDot(tpnme.Object )
100103
104+ /** Tree representing `java.lang.Record` */
105+ def javaLangRecord (): Tree = javaLangDot(tpnme.Record )
106+
101107 def arrayOf (tpt : Tree ): AppliedTypeTree =
102108 AppliedTypeTree (scalaDot(tpnme.Array ), List (tpt))
103109
@@ -555,6 +561,14 @@ object JavaParsers {
555561
556562 def definesInterface (token : Int ): Boolean = token == INTERFACE || token == AT
557563
564+ /** If the next token is the identifier "record", convert it into the RECORD token.
565+ * This makes it easier to handle records in various parts of the code,
566+ * in particular when a `parentToken` is passed to some functions.
567+ */
568+ def adaptRecordIdentifier (): Unit =
569+ if in.token == IDENTIFIER && in.name == jnme.RECORDid then
570+ in.token = RECORD
571+
558572 def termDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = {
559573 val inInterface = definesInterface(parentToken)
560574 val tparams = if (in.token == LT ) typeParams(Flags .JavaDefined | Flags .Param ) else List ()
@@ -581,6 +595,16 @@ object JavaParsers {
581595 TypeTree (), methodBody()).withMods(mods)
582596 }
583597 }
598+ } else if (in.token == LBRACE && rtptName != nme.EMPTY && parentToken == RECORD ) {
599+ /*
600+ record RecordName(T param1, ...) {
601+ RecordName { // <- here
602+ // methodBody
603+ }
604+ }
605+ */
606+ methodBody()
607+ Nil
584608 }
585609 else {
586610 var mods1 = mods
@@ -717,12 +741,11 @@ object JavaParsers {
717741 ValDef (name, tpt2, if (mods.is(Flags .Param )) EmptyTree else unimplementedExpr).withMods(mods1)
718742 }
719743
720- def memberDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = in.token match {
721- case CLASS | ENUM | INTERFACE | AT =>
722- typeDecl(start, if ( definesInterface(parentToken)) mods | Flags .JavaStatic else mods)
744+ def memberDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = in.token match
745+ case CLASS | ENUM | RECORD | INTERFACE | AT =>
746+ typeDecl(start, if definesInterface(parentToken) then mods | Flags .JavaStatic else mods)
723747 case _ =>
724748 termDecl(start, mods, parentToken, parentTParams)
725- }
726749
727750 def makeCompanionObject (cdef : TypeDef , statics : List [Tree ]): Tree =
728751 atSpan(cdef.span) {
@@ -804,6 +827,49 @@ object JavaParsers {
804827 addCompanionObject(statics, cls)
805828 }
806829
830+ def recordDecl (start : Offset , mods : Modifiers ): List [Tree ] =
831+ accept(RECORD )
832+ val nameOffset = in.offset
833+ val name = identForType()
834+ val tparams = typeParams()
835+ val header = formalParams()
836+ val superclass = javaLangRecord() // records always extend java.lang.Record
837+ val interfaces = interfacesOpt() // records may implement interfaces
838+ val (statics, body) = typeBody(RECORD , name, tparams)
839+
840+ // We need to generate accessors for every param, if no method with the same name is already defined
841+
842+ var fieldsByName = header.map(v => (v.name, (v.tpt, v.mods.annotations))).to(ListMap )
843+
844+ for case DefDef (name, paramss, _, _) <- body
845+ if paramss.isEmpty && fieldsByName.contains(name)
846+ do
847+ fieldsByName -= name
848+ end for
849+
850+ val accessors =
851+ (for (name, (tpt, annots)) <- fieldsByName yield
852+ DefDef (name, Nil , tpt, unimplementedExpr)
853+ .withMods(Modifiers (Flags .JavaDefined | Flags .Method | Flags .Synthetic ))
854+ ).toList
855+
856+ // generate the canonical constructor
857+ val canonicalConstructor = makeConstructor(header, tparams)
858+
859+ // return the trees
860+ val recordTypeDef = atSpan(start, nameOffset) {
861+ TypeDef (name,
862+ makeTemplate(
863+ parents = superclass :: interfaces,
864+ stats = canonicalConstructor :: accessors ::: body,
865+ tparams = tparams,
866+ false
867+ )
868+ )
869+ }
870+ addCompanionObject(statics, recordTypeDef)
871+ end recordDecl
872+
807873 def interfaceDecl (start : Offset , mods : Modifiers ): List [Tree ] = {
808874 accept(INTERFACE )
809875 val nameOffset = in.offset
@@ -846,7 +912,8 @@ object JavaParsers {
846912 else if (in.token == SEMI )
847913 in.nextToken()
848914 else {
849- if (in.token == ENUM || definesInterface(in.token)) mods |= Flags .JavaStatic
915+ adaptRecordIdentifier()
916+ if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) mods |= Flags .JavaStatic
850917 val decls = memberDecl(start, mods, parentToken, parentTParams)
851918 (if (mods.is(Flags .JavaStatic ) || inInterface && ! (decls exists (_.isInstanceOf [DefDef ])))
852919 statics
@@ -947,13 +1014,13 @@ object JavaParsers {
9471014 }
9481015 }
9491016
950- def typeDecl (start : Offset , mods : Modifiers ): List [Tree ] = in.token match {
1017+ def typeDecl (start : Offset , mods : Modifiers ): List [Tree ] = in.token match
9511018 case ENUM => enumDecl(start, mods)
9521019 case INTERFACE => interfaceDecl(start, mods)
9531020 case AT => annotationDecl(start, mods)
9541021 case CLASS => classDecl(start, mods)
1022+ case RECORD => recordDecl(start, mods)
9551023 case _ => in.nextToken(); syntaxError(em " illegal start of type declaration " , skipIt = true ); List (errorTypeTree)
956- }
9571024
9581025 def tryConstant : Option [Constant ] = {
9591026 val negate = in.token match {
@@ -1004,6 +1071,7 @@ object JavaParsers {
10041071 if (in.token != EOF ) {
10051072 val start = in.offset
10061073 val mods = modifiers(inInterface = false )
1074+ adaptRecordIdentifier() // needed for typeDecl
10071075 buf ++= typeDecl(start, mods)
10081076 }
10091077 }
0 commit comments