summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/reflect/internal/Definitions.scala17
-rw-r--r--src/compiler/scala/reflect/internal/transform/Erasure.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala68
-rw-r--r--test/files/neg/main1.check20
-rw-r--r--test/files/neg/t4749.check28
-rw-r--r--test/files/neg/t4749.flags1
-rw-r--r--test/files/neg/t4749.scala44
7 files changed, 137 insertions, 43 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala
index d3af8e2623..cc6ba32f9c 100644
--- a/src/compiler/scala/reflect/internal/Definitions.scala
+++ b/src/compiler/scala/reflect/internal/Definitions.scala
@@ -443,15 +443,11 @@ trait Definitions extends reflect.api.StandardDefinitions {
def isStringAddition(sym: Symbol) = sym == String_+ || sym == StringAdd_+
def isArrowAssoc(sym: Symbol) = ArrowAssocClass.tpe.decls.toList contains sym
- // The given symbol is a method with the right signature to be a runnable java program.
- def isJavaMainMethod(sym: Symbol) = sym.tpe match {
- case MethodType(param :: Nil, restpe) if restpe.typeSymbol == UnitClass =>
- param.tpe match {
- case TypeRef(_, ArrayClass, arg :: Nil) => arg.typeSymbol == StringClass
- case _ => false
- }
- case _ => false
- }
+ // The given symbol is a method with the right name and signature to be a runnable java program.
+ def isJavaMainMethod(sym: Symbol) = (sym.name == nme.main) && (sym.info match {
+ case MethodType(p :: Nil, restpe) => isArrayOfSymbol(p.tpe, StringClass) && restpe.typeSymbol == UnitClass
+ case _ => false
+ })
// The given class has a main method.
def hasJavaMainMethod(sym: Symbol): Boolean =
(sym.tpe member nme.main).alternatives exists isJavaMainMethod
@@ -595,6 +591,9 @@ trait Definitions extends reflect.api.StandardDefinitions {
def byNameType(arg: Type) = appliedType(ByNameParamClass.typeConstructor, List(arg))
def iteratorOfType(tp: Type) = appliedType(IteratorClass.typeConstructor, List(tp))
+ lazy val StringArray = arrayType(StringClass.tpe)
+ lazy val ObjectArray = arrayType(ObjectClass.tpe)
+
def ClassType(arg: Type) =
if (phase.erasedTypes || forMSIL) ClassClass.tpe
else appliedType(ClassClass.typeConstructor, List(arg))
diff --git a/src/compiler/scala/reflect/internal/transform/Erasure.scala b/src/compiler/scala/reflect/internal/transform/Erasure.scala
index c8cb6febfa..1e0ba17e15 100644
--- a/src/compiler/scala/reflect/internal/transform/Erasure.scala
+++ b/src/compiler/scala/reflect/internal/transform/Erasure.scala
@@ -75,7 +75,7 @@ trait Erasure {
case TypeRef(pre, sym, args) =>
if (sym == ArrayClass)
if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe
- else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectClass.tpe)
+ else if (args.head.typeSymbol.isBottomClass) ObjectArray
else typeRef(apply(pre), sym, args map this)
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) erasedTypeRef(ObjectClass)
else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 03d1bc3ad2..4ec3ef5ae6 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -30,13 +30,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
import global._
import icodes._
import icodes.opcodes._
- import definitions.{
- AnyClass, ObjectClass, ThrowsClass, ThrowableClass, ClassfileAnnotationClass,
- SerializableClass, StringClass, ClassClass, FunctionClass,
- DeprecatedAttr, SerializableAttr, SerialVersionUIDAttr, VolatileAttr,
- TransientAttr, CloneableAttr, RemoteAttr, JavaCloneableClass,
- RemoteInterfaceClass, RemoteExceptionClass, hasJavaMainMethod
- }
+ import definitions._
val phaseName = "jvm"
@@ -68,10 +62,10 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
def isJavaEntryPoint(clasz: IClass) = {
val sym = clasz.symbol
- def fail(msg: String) = {
+ def fail(msg: String, pos: Position = sym.pos) = {
clasz.cunit.warning(sym.pos,
- sym.name + " has a main method, but " + sym.fullName('.') + " will not be a runnable program.\n" +
- " " + msg + ", which means no static forwarder can be generated.\n"
+ sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" +
+ " Reason: " + msg
// TODO: make this next claim true, if possible
// by generating valid main methods as static in module classes
// not sure what the jvm allows here
@@ -79,19 +73,47 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
)
false
}
- sym.hasModuleFlag && hasJavaMainMethod(sym) && {
- // At this point we've seen a module with a main method, so if this
- // doesn't turn out to be a valid entry point, issue a warning.
- val companion = sym.linkedClassOfClass
- if (companion.isTrait)
- fail("Its companion is a trait")
- else if (hasJavaMainMethod(companion) && !(sym isSubClass companion))
- fail("Its companion contains its own main method")
- // this is only because forwarders aren't smart enough yet
- else if (companion.tpe.member(nme.main) != NoSymbol)
- fail("Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
- else
- true
+ def failNoForwarder(msg: String) = {
+ fail(msg + ", which means no static forwarder can be generated.\n")
+ }
+ val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil
+ val hasApproximate = possibles exists { m =>
+ m.info match {
+ case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass
+ case _ => false
+ }
+ }
+ // At this point it's a module with a main-looking method, so either
+ // succeed or warn that it isn't.
+ hasApproximate && {
+ // Before erasure so we can identify generic mains.
+ atPhase(currentRun.erasurePhase) {
+ val companion = sym.linkedClassOfClass
+ val companionMain = companion.tpe.member(nme.main)
+
+ if (hasJavaMainMethod(companion))
+ failNoForwarder("companion contains its own main method")
+ else if (companion.tpe.member(nme.main) != NoSymbol)
+ // this is only because forwarders aren't smart enough yet
+ failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
+ else if (companion.isTrait)
+ failNoForwarder("companion is a trait")
+ // Now either succeeed, or issue some additional warnings for things which look like
+ // attempts to be java main methods.
+ else possibles exists { m =>
+ m.info match {
+ case PolyType(_, _) =>
+ fail("main methods cannot be generic.")
+ case MethodType(params, res) =>
+ if (res.typeSymbol :: params exists (_.isAbstractType))
+ fail("main methods cannot refer to type parameters or abstract types.", m.pos)
+ else
+ isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos)
+ case tp =>
+ fail("don't know what this is: " + tp, m.pos)
+ }
+ }
+ }
}
}
diff --git a/test/files/neg/main1.check b/test/files/neg/main1.check
index 734c78e54d..1a7a13e1e9 100644
--- a/test/files/neg/main1.check
+++ b/test/files/neg/main1.check
@@ -1,25 +1,25 @@
-main1.scala:3: error: Foo has a main method, but foo1.Foo will not be a runnable program.
- Its companion is a trait, which means no static forwarder can be generated.
+main1.scala:3: error: Foo has a main method with parameter type Array[String], but foo1.Foo will not be a runnable program.
+ Reason: companion is a trait, which means no static forwarder can be generated.
object Foo { // companion is trait
^
-main1.scala:10: error: Foo has a main method, but foo2.Foo will not be a runnable program.
- Its companion contains its own main method, which means no static forwarder can be generated.
+main1.scala:10: error: Foo has a main method with parameter type Array[String], but foo2.Foo will not be a runnable program.
+ Reason: companion contains its own main method, which means no static forwarder can be generated.
object Foo { // companion has its own main
^
-main1.scala:22: error: Foo has a main method, but foo3.Foo will not be a runnable program.
- Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature), which means no static forwarder can be generated.
+main1.scala:22: error: Foo has a main method with parameter type Array[String], but foo3.Foo will not be a runnable program.
+ Reason: companion contains its own main method (implementation restriction: no main is allowed, regardless of signature), which means no static forwarder can be generated.
object Foo { // Companion contains main, but not an interfering main.
^
-main1.scala:31: error: Foo has a main method, but foo4.Foo will not be a runnable program.
- Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature), which means no static forwarder can be generated.
+main1.scala:31: error: Foo has a main method with parameter type Array[String], but foo4.Foo will not be a runnable program.
+ Reason: companion contains its own main method, which means no static forwarder can be generated.
object Foo extends Foo { // Inherits main from the class
^
-main1.scala:39: error: Foo has a main method, but foo5.Foo will not be a runnable program.
- Its companion contains its own main method (implementation restriction: no main is allowed, regardless of signature), which means no static forwarder can be generated.
+main1.scala:39: error: Foo has a main method with parameter type Array[String], but foo5.Foo will not be a runnable program.
+ Reason: companion contains its own main method, which means no static forwarder can be generated.
object Foo extends Foo { // Overrides main from the class
^
diff --git a/test/files/neg/t4749.check b/test/files/neg/t4749.check
new file mode 100644
index 0000000000..93ad3935fa
--- /dev/null
+++ b/test/files/neg/t4749.check
@@ -0,0 +1,28 @@
+t4749.scala:2: error: Fail1 has a main method with parameter type Array[String], but bippy.Fail1 will not be a runnable program.
+ Reason: main method must have exact signature (Array[String])Unit
+ object Fail1 {
+ ^
+t4749.scala:6: error: Fail2 has a main method with parameter type Array[String], but bippy.Fail2 will not be a runnable program.
+ Reason: main methods cannot be generic.
+ object Fail2 {
+ ^
+t4749.scala:13: error: Fail3 has a main method with parameter type Array[String], but bippy.Fail3 will not be a runnable program.
+ Reason: main methods cannot refer to type parameters or abstract types.
+ object Fail3 extends Bippy[Unit] { }
+ ^
+t4749.scala:16: error: Fail4 has a main method with parameter type Array[String], but bippy.Fail4 will not be a runnable program.
+ Reason: companion is a trait, which means no static forwarder can be generated.
+
+ object Fail4 {
+ ^
+t4749.scala:21: error: Fail5 has a main method with parameter type Array[String], but bippy.Fail5 will not be a runnable program.
+ Reason: companion contains its own main method, which means no static forwarder can be generated.
+
+ object Fail5 extends Fail5 { }
+ ^
+t4749.scala:26: error: Fail6 has a main method with parameter type Array[String], but bippy.Fail6 will not be a runnable program.
+ Reason: companion contains its own main method (implementation restriction: no main is allowed, regardless of signature), which means no static forwarder can be generated.
+
+ object Fail6 {
+ ^
+6 errors found
diff --git a/test/files/neg/t4749.flags b/test/files/neg/t4749.flags
new file mode 100644
index 0000000000..e8fb65d50c
--- /dev/null
+++ b/test/files/neg/t4749.flags
@@ -0,0 +1 @@
+-Xfatal-warnings \ No newline at end of file
diff --git a/test/files/neg/t4749.scala b/test/files/neg/t4749.scala
new file mode 100644
index 0000000000..0973c36097
--- /dev/null
+++ b/test/files/neg/t4749.scala
@@ -0,0 +1,44 @@
+package bippy {
+ object Fail1 {
+ def main(args: Array[String]): Any = ()
+ }
+
+ object Fail2 {
+ def main[T](args: Array[String]): T = null.asInstanceOf[T]
+ }
+
+ abstract class Bippy[T] {
+ def main(args: Array[String]): T = null.asInstanceOf[T]
+ }
+ object Fail3 extends Bippy[Unit] { }
+
+
+ object Fail4 {
+ def main(args: Array[String]): Unit = ()
+ }
+ trait Fail4 { }
+
+ object Fail5 extends Fail5 { }
+ class Fail5 {
+ def main(args: Array[String]): Unit = ()
+ }
+
+ object Fail6 {
+ def main(args: Array[String]): Unit = ()
+ }
+ class Fail6 {
+ def main = "bippy"
+ }
+
+ object Win1 {
+ def main(args: Array[String]): Unit = ()
+ }
+ object Win2 extends Bippy[Unit] {
+ override def main(args: Array[String]): Unit = ()
+ }
+ trait WinBippy[T] {
+ def main(args: Array[String]): T = null.asInstanceOf[T]
+ }
+ object Win3 extends WinBippy[Unit] { }
+}
+