From b2c67b328daeaf51eacdb0333db85a7287b5fe1f Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 26 Apr 2013 13:31:48 -0700 Subject: SI-7398 Add support for java8 default methods In classfile parser: mark symbols which represent interface methods yet have code attributes with new flag DEFAULTMETHOD. These must be kept distinct from regular method bodies so that an error can be issued when a regular concrete method is overridden without the override keyword, but not when the overridden method is a default. In java source parser: mark Modifiers of interface default methods with DEFAULTMETHOD flag. Writing the test was everything I dreamed, and more! However, % test/partest --debug test/files/run/t7398.scala Skipping java8-specific test under java version 1.7.0_21 testing: [...]/files/run/t7398.scala [ OK ] All of 1 tests were successful (elapsed time: 00:00:04) % test/partest --debug test/files/run/t7398.scala Attempting java8-specific test under java version 1.8.0-ea testing: [...]/files/run/t7398.scala [ OK ] All of 1 tests were successful (elapsed time: 00:00:13) --- src/compiler/scala/tools/nsc/javac/JavaParsers.scala | 6 +++++- .../scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 6 ++++++ src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 2 +- src/reflect/scala/reflect/internal/Flags.scala | 12 +++++++----- 4 files changed, 19 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 8aa9b81a72..0779e648cd 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -420,6 +420,9 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { case FINAL => flags |= Flags.FINAL in.nextToken + case DEFAULT => + flags |= Flags.DEFAULTMETHOD + in.nextToken() case NATIVE => addAnnot(NativeAttr) in.nextToken @@ -544,8 +547,9 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val vparams = formalParams() if (!isVoid) rtpt = optArrayBrackets(rtpt) optThrows() + val bodyOk = !inInterface || (mods hasFlag Flags.DEFAULTMETHOD) val body = - if (!inInterface && in.token == LBRACE) { + if (bodyOk && in.token == LBRACE) { methodBody() } else { if (parentToken == AT && in.token == DEFAULT) { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index fb2301de65..0ae2b501f0 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -931,6 +931,12 @@ abstract class ClassfileParser { case pkg => pkg.fullName(File.separatorChar)+File.separator+srcfileLeaf } srcfile0 = settings.outputDirs.srcFilesFor(in.file, srcpath).find(_.exists) + case tpnme.CodeATTR => + if (sym.owner.isInterface) { + sym setFlag DEFAULTMETHOD + log(s"$sym in ${sym.owner} is a java8+ default method.") + } + in.skip(attrLen) case _ => in.skip(attrLen) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 7f8aeceeec..396a2716d2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -383,7 +383,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans overrideError("cannot be used here - classes can only override abstract types"); } else if (other.isEffectivelyFinal) { // (1.2) overrideError("cannot override final member"); - } else if (!other.isDeferred && !member.isAnyOverride && !member.isSynthetic) { // (*) + } else if (!other.isDeferred && !other.hasFlag(DEFAULTMETHOD) && !member.isAnyOverride && !member.isSynthetic) { // (*) // (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. if (isNeitherInClass && !(other.owner isSubClass member.owner)) diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index 86cbba9c50..5ebe02d95d 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -59,9 +59,9 @@ import scala.collection.{ mutable, immutable } // 42: VBRIDGE // 43: VARARGS // 44: TRIEDCOOKING -// 45: -// 46: -// 47: +// 45: SYNCHRONIZED/M +// 46: ARTIFACT +// 47: DEFAULTMETHOD/M // 48: // 49: // 50: @@ -116,6 +116,8 @@ class ModifierFlags { final val LAZY = 1L << 31 // symbol is a lazy val. can't have MUTABLE unless transformed by typer final val PRESUPER = 1L << 37 // value is evaluated before super call final val DEFAULTINIT = 1L << 41 // symbol is initialized to the default value: used by -Xcheckinit + // ARTIFACT at #46 in 2.11+ + final val DEFAULTMETHOD = 1L << 47 // symbol is a java default method // Overridden. def flagToString(flag: Long): String = "" @@ -239,7 +241,7 @@ class Flags extends ModifierFlags { */ final val ExplicitFlags = PRIVATE | PROTECTED | ABSTRACT | FINAL | SEALED | - OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE | LAZY + OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE | LAZY | DEFAULTMETHOD /** The two bridge flags */ final val BridgeFlags = BRIDGE | VBRIDGE @@ -421,7 +423,7 @@ class Flags extends ModifierFlags { case TRIEDCOOKING => "" // (1L << 44) case SYNCHRONIZED => "" // (1L << 45) case 0x400000000000L => "" // (1L << 46) - case 0x800000000000L => "" // (1L << 47) + case DEFAULTMETHOD => "" // (1L << 47) case 0x1000000000000L => "" // (1L << 48) case 0x2000000000000L => "" // (1L << 49) case 0x4000000000000L => "" // (1L << 50) -- cgit v1.2.3