From b941551529e40fc7d71cf25e1ad904ab7badd14c Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 22 May 2013 15:51:18 +0200 Subject: SI-6138 Centralize and refine detection of `getClass` calls `getClass` is special cased in the compiler; this is described in in the comments on `Definitions.Any_getClass`. Part of this happens in `Typer#stabilize`. This was trying to determine if an Ident or Select node was a call to `getClass` by merits of the name of the tree's symbol and by checking that the its type (if it was a MethodType or PolyType) had no parameters in the primary parameter list. Overloaded user defined `getClass` methods confused this check. In the enclosed test case, the tree `definitions.this.getClass` had an `OverloadedType`, and such types always report an empty list of `params`. This commit: - changes `stabilize` to use `isGetClass`, rather than the homebrew check - changes `isGetClass` to consider a `Set[Symbol]` containing all `getClass` variants. This moves some similar code from `Erasure` to `Definitions` - keeps a fast negative path in `isGetClass` based on the symbol's name --- bincompat-backward.whitelist.conf | 8 ++++++++ bincompat-forward.whitelist.conf | 8 ++++++++ src/compiler/scala/tools/nsc/transform/Erasure.scala | 5 ----- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 3 +-- src/reflect/scala/reflect/internal/Definitions.scala | 12 ++++++++++-- test/files/neg/t6138.check | 7 +++++++ test/files/neg/t6138.scala | 5 +++++ 7 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 test/files/neg/t6138.check create mode 100644 test/files/neg/t6138.scala diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index 904e9477a2..2ef4ae1cc1 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -219,6 +219,14 @@ filter { { matchName="scala.concurrent.forkjoin.ForkJoinPool.helpJoinOnce" problemName=IncompatibleResultTypeProblem + }, + { + matchName="scala.reflect.internal.Definitions#DefinitionsClass.getClassMethods" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Definitions#DefinitionsClass.primitiveGetClassMethods" + problemName=MissingMethodProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index 83864f48ee..284e0809a8 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -471,6 +471,14 @@ filter { { matchName="scala.concurrent.impl.Promise$CompletionLatch" problemName=MissingClassProblem + }, + { + matchName="scala.reflect.internal.Definitions#DefinitionsClass.getClassMethods" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Definitions#DefinitionsClass.primitiveGetClassMethods" + problemName=MissingMethodProblem } ] } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index ead6ef288c..76249974ac 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -341,11 +341,6 @@ abstract class Erasure extends AddInterfaces } } - // Each primitive value class has its own getClass for ultra-precise class object typing. - private lazy val primitiveGetClassMethods = Set[Symbol](Any_getClass, AnyVal_getClass) ++ ( - ScalaValueClasses map (_.tpe member nme.getClass_) - ) - // ## requires a little translation private lazy val poundPoundMethods = Set[Symbol](Any_##, Object_##) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index c59ef4ebda..29cd3d4bfa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -656,8 +656,7 @@ trait Typers extends Modes with Adaptations with Tags { // To fully benefit from special casing the return type of // getClass, we have to catch it immediately so expressions // like x.getClass().newInstance() are typed with the type of x. - else if ( tree.symbol.name == nme.getClass_ - && tree.tpe.params.isEmpty + else if ( isGetClass(tree.symbol) // TODO: If the type of the qualifier is inaccessible, we can cause private types // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this // so for now it requires the type symbol be public. diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index e5d9e54a16..629d82d254 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -110,8 +110,10 @@ trait Definitions extends api.StandardDefinitions { /** Is symbol a numeric value class? */ def isNumericValueClass(sym: Symbol) = ScalaNumericValueClasses contains sym - def isGetClass(sym: Symbol) = - (sym.name == nme.getClass_) && flattensToEmpty(sym.paramss) + def isGetClass(sym: Symbol) = ( + sym.name == nme.getClass_ // this condition is for performance only, this is called from `Typer#stabliize`. + && getClassMethods(sym) + ) lazy val UnitClass = valueClassSymbol(tpnme.Unit) lazy val ByteClass = valueClassSymbol(tpnme.Byte) @@ -805,6 +807,12 @@ trait Definitions extends api.StandardDefinitions { lazy val Any_isInstanceOf = newT1NullaryMethod(AnyClass, nme.isInstanceOf_, FINAL)(_ => booltype) lazy val Any_asInstanceOf = newT1NullaryMethod(AnyClass, nme.asInstanceOf_, FINAL)(_.typeConstructor) + lazy val primitiveGetClassMethods = Set[Symbol](Any_getClass, AnyVal_getClass) ++ ( + ScalaValueClasses map (_.tpe member nme.getClass_) + ) + + lazy val getClassMethods: Set[Symbol] = primitiveGetClassMethods + Object_getClass + // A type function from T => Class[U], used to determine the return // type of getClass calls. The returned type is: // diff --git a/test/files/neg/t6138.check b/test/files/neg/t6138.check new file mode 100644 index 0000000000..8fd9978248 --- /dev/null +++ b/test/files/neg/t6138.check @@ -0,0 +1,7 @@ +t6138.scala:4: error: ambiguous reference to overloaded definition, +both method getClass in object definitions of type (s: Int)Any +and method getClass in object definitions of type (s: String)Any +match argument types (Nothing) + getClass(???): String + ^ +one error found diff --git a/test/files/neg/t6138.scala b/test/files/neg/t6138.scala new file mode 100644 index 0000000000..2f45a46b1c --- /dev/null +++ b/test/files/neg/t6138.scala @@ -0,0 +1,5 @@ +object definitions { + def getClass(s: String): Any = ??? + def getClass(s: Int): Any = ??? + getClass(???): String +} -- cgit v1.2.3