diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2009-08-21 16:15:58 +0000 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2009-08-21 16:15:58 +0000 |
commit | 78b61c43da92f9af2bcd09b61ed08c824f37cd8b (patch) | |
tree | 90002e74ed089a3575ade9e6e14a5a56b48a7b00 /src | |
parent | f73e819a415ecd290c5464206a4ef2a4080075f5 (diff) | |
download | scala-78b61c43da92f9af2bcd09b61ed08c824f37cd8b.tar.gz scala-78b61c43da92f9af2bcd09b61ed08c824f37cd8b.tar.bz2 scala-78b61c43da92f9af2bcd09b61ed08c824f37cd8b.zip |
replaced the implicit `identity` coercion by `c...
replaced the implicit `identity` coercion by `conforms`, which can be
used to encode generalised constraints the introduction of `conforms`
revealed a bug in adaptToMember, which was inferring views while already
inferring one, which gave rise to diverging implicits. Predef.identity
is no longer special as far as the compiler is concerned.
because conforms/identity was no longer prevented from being used as
a view (which does not make sense, but preventing it shouldn't be
necessary), removeNames in NamesDefaults suddenly didn't detect all
ambiguities because it relied on tryTypedApply failing fixed by using an
EmptyTree as an ambiguous argument instead of the argument, so failure
is guaranteed
fixed check file for t0590
new starr
fixed the weirdest bug ever: don't know why, but can't change the total
number of calls to newTermName in StdNames (so take away the one for
"identity", give one back, doesn't matter where --> see "utterweirdness"
at the end) the problem manifested itself by not finding Nil. This only
happens during start up (when the scala/package.scala file hasn't been
compiled yet), when Nil is required before List (because that would have
forced Nil to be loaded).
Diffstat (limited to 'src')
6 files changed, 38 insertions, 13 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 150cc9a12d..0f6fd6a944 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -111,7 +111,6 @@ trait Definitions { if (!ClassClass.unsafeTypeParams.isEmpty && !phase.erasedTypes) appliedType(ClassClass.tpe, List(classType)) else ClassClass.tpe - def Predef_identity = getMember(PredefModule, nme.identity) def Predef_error = getMember(PredefModule, nme.error) lazy val ConsoleModule: Symbol = getModule("scala.Console") lazy val ScalaRunTimeModule: Symbol = getModule("scala.runtime.ScalaRunTime") diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index baeea7273a..885af91fdb 100644 --- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -295,7 +295,6 @@ trait StdNames { val hashCode_ = newTermName("hashCode") val hasNext = newTermName("hasNext") val head = newTermName("head") - val identity = newTermName("identity") val invoke_ = newTermName("invoke") val isInstanceOf_ = newTermName("isInstanceOf") val isDefinedAt = newTermName("isDefinedAt") @@ -403,6 +402,9 @@ trait StdNames { // Stores Java annotations on parameters with RetentionPolicy.RUNTIME val RuntimeParamAnnotationATTR = newTermName("RuntimeVisibleParameterAnnotations") val ScalaATTR = newTermName("Scala") + + newTermName("utterweirdness") // (adriaan) removed newTermName("identity"), + // but if I don't give something in return, quick.lib (after newstarr) fails with "error: not found: value Nil" } def encode(str: String): Name = newTermName(NameTransformer.encode(str)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index dc00955e0b..bce3c4a5ee 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -247,16 +247,17 @@ self: Analyzer => private def typedImplicit(info: ImplicitInfo): SearchResult = context.openImplicits find (dominates(pt, _)) match { case Some(pending) => - //println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) + // println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG throw DivergentImplicit SearchFailure case None => try { context.openImplicits = pt :: context.openImplicits - //println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) + // println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG typedImplicit0(info) } catch { case DivergentImplicit => + // println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG if (context.openImplicits.tail.isEmpty) { if (!(pt.isErroneous)) context.unit.error( @@ -413,16 +414,17 @@ self: Analyzer => * This is the case if all of the following holds: * - the info's type is not erroneous, * - the info is not shadowed by another info with the same name, - * - if the symbol is Predef.identity, then we are not looking for a view, * - the result of typedImplicit is non-empty. * @return A search result with an attributed tree containing the implicit if succeeded, * SearchFailure if not. */ def tryImplicit(info: ImplicitInfo): SearchResult = - if (!containsError(info.tpe) && - !(isLocal && shadowed.contains(info.name)) && - (!isView || info.sym != Predef_identity)) typedImplicit(info) - else SearchFailure + if (containsError(info.tpe) || + (isLocal && shadowed.contains(info.name)) //|| + // (isView && (info.sym == Predef_identity || info.sym == Predef_conforms})) //@M this condition prevents no-op conversions, which are a problem (besides efficiency), + // one example is removeNames in NamesDefaults, which relies on the type checker failing in case of ambiguity between an assignment/named arg + ) SearchFailure + else typedImplicit(info) def appInfos(is: List[ImplicitInfo]): Map[ImplicitInfo, SearchResult] = { var applicable = Map[ImplicitInfo, SearchResult]() diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 482c53fac5..9d485cc730 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -390,9 +390,16 @@ trait NamesDefaults { self: Analyzer => argPos(index) = pos rhs case t: Tree => - errorTree(arg, "reference to "+ name +" is ambiguous; it is both, a parameter\n"+ + //@M was: errorTree(arg, ...) + // this throws an exception that's caught in `tryTypedApply` (as it uses `silent`) + // unfortunately, tryTypedApply recovers from the exception if you use errorTree(arg, ...) and conforms is allowed as a view (see tryImplicit in Implicits) + // because it tries to produce a new qualifier (if the old one was P, the new one will be conforms.apply(P)), and if that works, it pretends nothing happened + // so, to make sure tryTypedApply fails, pass EmptyTree instead + errorTree(EmptyTree, "reference to "+ name +" is ambiguous; it is both, a parameter\n"+ "name of the method and the name of a variable currently in scope.") } + //@M note that we don't get here when an ambiguity was detected (during the computation of res), + // as errorTree throws an exception typer.context.undetparams = udp res } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 745d1c8241..b609f1f24b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -991,7 +991,12 @@ trait Typers { self: Analyzer => if (qual.isTerm && ((qual.symbol eq null) || !qual.symbol.isTerm || qual.symbol.isValue) && phase.id <= currentRun.typerPhase.id && !qtpe.isError && !tp.isError && - qtpe.typeSymbol != NullClass && qtpe.typeSymbol != NothingClass && qtpe != WildcardType) { + qtpe.typeSymbol != NullClass && qtpe.typeSymbol != NothingClass && qtpe != WildcardType && + context.implicitsEnabled) { // don't try to adapt a top-level type that's the subject of an implicit search + // this happens because, if isView, typedImplicit tries to apply the "current" implicit value to + // a value that needs to be coerced, so we check whether the implicit value has an `apply` method + // (if we allow this, we get divergence, e.g., starting at `conforms` during ant quick.bin) + // note: implicit arguments are still inferred (this kind of "chaining" is allowed) val coercion = inferView(qual, qtpe, name, tp) if (coercion != EmptyTree) typedQualifier(atPos(qual.pos)(Apply(coercion, List(qual)))) diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala index 565aa363e8..aa718e1892 100644 --- a/src/library/scala/Predef.scala +++ b/src/library/scala/Predef.scala @@ -76,6 +76,10 @@ object Predef { val Map = collection.immutable.Map val Set = collection.immutable.Set + // no longer a view: subsumed by `conforms` (which is less likely to give rise to ambiguities) + // @see `conforms` for the implicit version + def identity[A](x: A): A = x + // errors and asserts ------------------------------------------------- def error(message: String): Nothing = throw new RuntimeException(message) @@ -180,8 +184,6 @@ object Predef { // views -------------------------------------------------------------- - implicit def identity[A](x: A): A = x - implicit def byteWrapper(x: Byte) = new runtime.RichByte(x) implicit def shortWrapper(x: Short) = new runtime.RichShort(x) implicit def intWrapper(x: Int) = new runtime.RichInt(x) @@ -249,5 +251,13 @@ object Predef { override def toString: String = xs.mkString("") } + // used, for example, in the encoding of generalized constraints + // we need a new type constructor `<:<` and evidence `conforms`, as + // reusing `Function2` and `identity` leads to ambiguities (any2stringadd is inferred) + // to constrain any abstract type T that's in scope in a method's argument list (not just the method's own type parameters) + // simply add an implicit argument of type `T <:< U`, where U is the required upper bound (for lower-bounds, use: `U <: T`) + sealed abstract class <:<[-From, +To] extends (From => To) + implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x} + def currentThread = java.lang.Thread.currentThread() } |