diff options
Diffstat (limited to 'src')
8 files changed, 146 insertions, 50 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index e05ac1087b..5b2c61701d 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -586,14 +586,6 @@ trait Definitions extends reflect.api.StandardDefinitions { case _ => NoType } - /** To avoid unchecked warnings on polymorphic classes, translate - * a Foo[T] into a Foo[_] for use in the pattern matcher. - */ - def typeCaseType(clazz: Symbol) = clazz.tpe.normalize match { - case TypeRef(_, sym, args) if args.nonEmpty => newExistentialType(sym.typeParams, clazz.tpe) - case tp => tp - } - def seqType(arg: Type) = appliedType(SeqClass.typeConstructor, List(arg)) def arrayType(arg: Type) = appliedType(ArrayClass.typeConstructor, List(arg)) def byNameType(arg: Type) = appliedType(ByNameParamClass.typeConstructor, List(arg)) @@ -609,6 +601,26 @@ trait Definitions extends reflect.api.StandardDefinitions { def vmClassType(arg: Type): Type = ClassType(arg) def vmSignature(sym: Symbol, info: Type): String = signature(info) // !!! + /** Given a class symbol C with type parameters T1, T2, ... Tn + * which have upper/lower bounds LB1/UB1, LB1/UB2, ..., LBn/UBn, + * returns an existential type of the form + * + * C[E1, ..., En] forSome { E1 >: LB1 <: UB1 ... en >: LBn <: UBn }. + */ + def classExistentialType(clazz: Symbol): Type = + newExistentialType(clazz.typeParams, clazz.tpe) + + /** Given type U, creates a Type representing Class[_ <: U]. + */ + def boundedClassType(upperBound: Type) = + appliedTypeAsUpperBounds(ClassClass.typeConstructor, List(upperBound)) + + /** To avoid unchecked warnings on polymorphic classes, translate + * a Foo[T] into a Foo[_] for use in the pattern matcher. + */ + @deprecated("Use classExistentialType", "2.10.0") + def typeCaseType(clazz: Symbol): Type = classExistentialType(clazz) + // // .NET backend // diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index fb827b0658..1973a97279 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -120,9 +120,11 @@ abstract class SymbolTable extends api.Universe try op finally phase = current } - - @inline final def afterPhase[T](ph: Phase)(op: => T): T = - atPhase(ph.next)(op) + /** Since when it is to be "at" a phase is inherently ambiguous, + * a couple unambiguously named methods. + */ + @inline final def beforePhase[T](ph: Phase)(op: => T): T = atPhase(ph)(op) + @inline final def afterPhase[T](ph: Phase)(op: => T): T = atPhase(ph.next)(op) @inline final def atPhaseNotLaterThan[T](target: Phase)(op: => T): T = if (target != NoPhase && phase.id > target.id) atPhase(target)(op) else op diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index cd44b700c1..6295c089b2 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -2433,25 +2433,37 @@ trait Types extends api.Types { self: SymbolTable => case _ => List() } - + /** An existential can only be printed with wildcards if: + * - the underlying type is a typeref + * - where there is a 1-to-1 correspondence between underlying's typeargs and quantified + * - and none of the existential parameters is referenced from anywhere else in the type + * - and none of the existential parameters are singleton types + */ + private def isRepresentableWithWildcards = !settings.debug.value && { + val qset = quantified.toSet + !qset.exists(_.isSingletonExistential) && (underlying match { + case TypeRef(_, sym, args) => + sameLength(args, quantified) && { + args forall { arg => + qset(arg.typeSymbol) && !qset.exists(arg.typeSymbol.info.bounds contains _) + } + } + case _ => false + }) + } override def safeToString: String = { - if (!(quantified exists (_.isSingletonExistential)) && !settings.debug.value) - // try to represent with wildcards first - underlying match { - case TypeRef(pre, sym, args) if args.nonEmpty => - val wargs = wildcardArgsString(quantified.toSet, args) - if (sameLength(wargs, args)) - return TypeRef(pre, sym, List()) + wargs.mkString("[", ", ", "]") - case _ => - } - var ustr = underlying.toString + def clauses = { + val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") + if (settings.explaintypes.value) "(" + str + ")" else str + } underlying match { - case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => ustr = "("+ustr+")" + case TypeRef(pre, sym, args) if isRepresentableWithWildcards => + "" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]") + case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => + "(" + underlying + ")" + clauses case _ => + "" + underlying + clauses } - val str = - ustr+(quantified map (_.existentialToString) mkString(" forSome { ", "; ", " }")) - if (settings.explaintypes.value) "("+str+")" else str } override def cloneInfo(owner: Symbol) = @@ -3260,6 +3272,25 @@ trait Types extends api.Types { self: SymbolTable => case WildcardType => tycon // needed for neg/t0226 case _ => abort(debugString(tycon)) } + + /** A creator for existential types where the type arguments, + * rather than being applied directly, are interpreted as the + * upper bounds of unknown types. For instance if the type argument + * list given is List(AnyRefClass), the resulting type would be + * e.g. Set[_ <: AnyRef] rather than Set[AnyRef] . + */ + def appliedTypeAsUpperBounds(tycon: Type, args: List[Type]): Type = { + tycon match { + case TypeRef(pre, sym, _) if sameLength(sym.typeParams, args) => + val eparams = typeParamsToExistentials(sym) + val bounds = args map (TypeBounds upper _) + (eparams, bounds).zipped foreach (_ setInfo _) + + newExistentialType(eparams, typeRef(pre, sym, eparams map (_.tpe))) + case _ => + appliedType(tycon, args) + } + } /** A creator for type parameterizations that strips empty type parameter lists. * Use this factory method to indicate the type has kind * (it's a polymorphic value) @@ -3845,6 +3876,8 @@ trait Types extends api.Types { self: SymbolTable => eparams map (_ substInfo (tparams, eparams)) } + def typeParamsToExistentials(clazz: Symbol): List[Symbol] = + typeParamsToExistentials(clazz, clazz.typeParams) // note: it's important to write the two tests in this order, // as only typeParams forces the classfile to be read. See #400 @@ -3876,7 +3909,7 @@ trait Types extends api.Types { self: SymbolTable => if (expanded contains sym) AnyRefClass.tpe else try { expanded += sym - val eparams = mapOver(typeParamsToExistentials(sym, sym.typeParams)) + val eparams = mapOver(typeParamsToExistentials(sym)) existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe))) } finally { expanded -= sym diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 18735cafe2..4493188b31 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -807,6 +807,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def currentRun: Run = curRun def currentUnit: CompilationUnit = if (currentRun eq null) NoCompilationUnit else currentRun.currentUnit def currentSource: SourceFile = if (currentUnit.exists) currentUnit.source else lastSeenSourceFile + + @inline final def afterTyper[T](op: => T): T = afterPhase(currentRun.typerPhase)(op) + @inline final def beforeErasure[T](op: => T): T = beforePhase(currentRun.erasurePhase)(op) + @inline final def afterErasure[T](op: => T): T = afterPhase(currentRun.erasurePhase)(op) /** Don't want to introduce new errors trying to report errors, * so swallow exceptions. @@ -1114,7 +1118,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb lazy val trackers = currentRun.units.toList map (x => SymbolTracker(x)) def snapshot() = { inform("\n[[symbol layout at end of " + phase + "]]") - atPhase(phase.next) { + afterPhase(phase) { trackers foreach { t => t.snapshot() inform(t.show("Heading from " + phase.prev.name + " to " + phase.name)) @@ -1389,7 +1393,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb def printAllUnits() { print("[[syntax trees at end of " + phase + "]]") - atPhase(phase.next) { currentRun.units foreach (treePrinter.print(_)) } + afterPhase(phase) { currentRun.units foreach (treePrinter.print(_)) } } private def findMemberFromRoot(fullName: Name): Symbol = { diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index fe479a5375..5f84d765b9 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -44,22 +44,16 @@ abstract class Erasure extends AddInterfaces // class object is that of java.lang.Integer, not Int. // // TODO: If T is final, return type could be Class[T]. Should it? - def getClassReturnType(tp: Type): Type = { - val sym = tp.typeSymbol - - if (phase.erasedTypes) ClassClass.tpe - else if (isValueClass(sym)) ClassType(tp.widen) - else { - val eparams = typeParamsToExistentials(ClassClass, ClassClass.typeParams) - val upperBound = ( - if (isPhantomClass(sym)) AnyClass.tpe + def getClassReturnType(tpe: Type): Type = { + if (phase.erasedTypes) ClassClass.tpe else { + val tp = tpe.widen.normalize + val sym = tp.typeSymbol + + if (isValueClass(sym)) ClassType(tp) + else boundedClassType( + if (isPhantomClass(sym)) ObjectClass.tpe else if (sym.isLocalClass) intersectionDominator(tp.parents) - else tp.widen - ) - - existentialAbstraction( - eparams, - ClassType(eparams.head setInfo TypeBounds.upper(upperBound) tpe) + else tp ) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 4ea21b1c44..cf90577959 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -116,7 +116,7 @@ trait SyntheticMethods extends ast.TreeDSL { */ def canEqualMethod: Tree = ( createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m => - Ident(m.firstParam) IS_OBJ typeCaseType(clazz)) + Ident(m.firstParam) IS_OBJ classExistentialType(clazz)) ) /** The equality method for case classes. @@ -132,7 +132,7 @@ trait SyntheticMethods extends ast.TreeDSL { */ def equalsClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => val arg0 = Ident(m.firstParam) - val thatTest = gen.mkIsInstanceOf(arg0, typeCaseType(clazz), true, false) + val thatTest = gen.mkIsInstanceOf(arg0, classExistentialType(clazz), true, false) val thatCast = gen.mkCast(arg0, clazz.tpe) def argsBody: Tree = { diff --git a/src/partest/scala/tools/partest/CompilerTest.scala b/src/partest/scala/tools/partest/CompilerTest.scala new file mode 100644 index 0000000000..dd06c051a4 --- /dev/null +++ b/src/partest/scala/tools/partest/CompilerTest.scala @@ -0,0 +1,27 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.partest + +import scala.tools.nsc._ + +/** For testing compiler internals directly. + * Each source code string in "sources" will be compiled, and + * the check function will be called with the source code and the + * resulting CompilationUnit. The check implementation should + * test for what it wants to test and fail (via assert or other + * exception) if it is not happy. + */ +abstract class CompilerTest extends DirectTest { + def check(source: String, unit: global.CompilationUnit): Unit + + lazy val global: Global = newCompiler() + lazy val units = compilationUnits(global)(sources: _ *) + + override def extraSettings = "-usejavacp -d " + testOutput.path + + def sources: List[String] = List(code) + def show() = (sources, units).zipped foreach check +} diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala index be8cac9147..74f511aa4e 100644 --- a/src/partest/scala/tools/partest/DirectTest.scala +++ b/src/partest/scala/tools/partest/DirectTest.scala @@ -35,13 +35,37 @@ abstract class DirectTest extends App { s processArguments (allArgs, true) s } - // compile the code, optionally first adding to the settings - def compile(args: String*) = { + // new compiler + def newCompiler(args: String*): Global = { val settings = newSettings((CommandLineParser tokenize extraSettings) ++ args.toList) - val global = new Global(settings) - new global.Run compileSources List(new BatchSourceFile("<partest>", code)) + new Global(settings) + } + def newSources(sourceCodes: String*) = sourceCodes.toList.zipWithIndex map { + case (src, idx) => new BatchSourceFile("newSource" + (idx + 1), src) + } + def compileString(global: Global)(sourceCode: String): Boolean = { + withRun(global)(_ compileSources newSources(sourceCode)) !global.reporter.hasErrors } + def compilationUnits(global: Global)(sourceCodes: String*): List[global.CompilationUnit] = { + val units = withRun(global) { run => + run compileSources newSources(sourceCodes: _*) + run.units.toList + } + if (global.reporter.hasErrors) { + global.reporter.flush() + sys.error("Compilation failure.") + } + units + } + + def withRun[T](global: Global)(f: global.Run => T): T = { + global.reporter.reset() + f(new global.Run) + } + + // compile the code, optionally first adding to the settings + def compile(args: String*) = compileString(newCompiler(args: _*))(code) /** Constructor/main body **/ try show() |