summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreePrinters.scala4
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala15
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala4
-rw-r--r--src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala12
-rw-r--r--src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala76
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala22
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala219
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala289
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala19
-rw-r--r--src/compiler/scala/tools/nsc/transform/TypingTransformers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala48
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala68
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala201
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala39
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala44
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala123
-rw-r--r--src/library/scala/annotation/implicitNotFound.scala18
-rw-r--r--src/library/scala/collection/generic/CanBuildFrom.scala3
-rw-r--r--test/files/neg/bug1275.check8
-rw-r--r--test/files/neg/bug1275.scala29
-rw-r--r--test/files/neg/bug882.check2
-rw-r--r--test/files/neg/t2416.check10
-rw-r--r--test/files/neg/t2416.scala14
-rw-r--r--test/files/neg/t2462a.check4
-rw-r--r--test/files/neg/t2462a.scala3
-rw-r--r--test/files/neg/t2462b.check14
-rw-r--r--test/files/neg/t2462b.scala12
-rw-r--r--test/files/neg/t3399.check4
-rw-r--r--test/files/neg/t3399.scala24
-rw-r--r--test/files/neg/t3507.check4
-rw-r--r--test/files/neg/t3507.scala15
-rw-r--r--test/files/neg/t3653.check7
-rw-r--r--test/files/neg/t3653.scala4
-rw-r--r--test/files/neg/t3663.check4
-rw-r--r--test/files/neg/t3663/PackageProtected.java5
-rw-r--r--test/files/neg/t3663/main.scala14
-rw-r--r--test/files/neg/t3691.check16
-rw-r--r--test/files/neg/t3691.scala11
-rw-r--r--test/files/neg/t742.check5
-rw-r--r--test/files/neg/t742.scala8
-rw-r--r--test/files/pos/nothing_manifest_disambig.scala10
-rw-r--r--test/files/pos/t2331.scala11
-rw-r--r--test/files/pos/t3249/Test.java5
-rw-r--r--test/files/pos/t3249/a.scala11
-rw-r--r--test/files/pos/t3374.scala6
-rw-r--r--test/files/pos/t3419/B_1.scala3
-rw-r--r--test/files/pos/t3419/C_2.scala3
-rw-r--r--test/files/pos/t3477.scala7
-rw-r--r--test/files/pos/t3486/JTest.java3
-rw-r--r--test/files/pos/t3486/test.scala6
-rw-r--r--test/files/pos/t3494.scala7
-rw-r--r--test/files/pos/t3582.scala12
-rw-r--r--test/files/pos/t3582b.scala5
-rw-r--r--test/files/pos/t3622/test/AsyncTask.java5
-rw-r--r--test/files/pos/t3622/test/MyAsyncTask.java9
-rw-r--r--test/files/pos/t3622/test/Test.scala5
-rw-r--r--test/files/pos/t3676.scala5
-rw-r--r--test/files/pos/t3777.scala7
61 files changed, 1042 insertions, 508 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 5044105684..41e93ae386 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -137,7 +137,7 @@ abstract class TreeGen {
assert(!tree.tpe.isInstanceOf[MethodType], tree)
assert(!pt.typeSymbol.isPackageClass)
assert(!pt.typeSymbol.isPackageObjectClass)
- assert(pt eq pt.normalize) //@MAT only called during erasure, which already takes care of that
+ assert(pt eq pt.normalize, tree +" : "+ debugString(pt) +" ~>"+ debugString(pt.normalize)) //@MAT only called during erasure, which already takes care of that
atPos(tree.pos)(mkAsInstanceOf(tree, pt, false))
}
diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
index ddc1c3169a..e1e7c90879 100644
--- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
@@ -379,6 +379,9 @@ trait TreePrinters { trees: SymbolTable =>
case SelectFromArray(qualifier, name, _) =>
print(qualifier); print(".<arr>"); print(symName(tree, name))
+ case TypeTreeWithDeferredRefCheck() =>
+ print("<tree with deferred refcheck>")
+
case tree =>
print("<unknown tree of class "+tree.getClass+">")
}
@@ -575,6 +578,7 @@ trait TreePrinters { trees: SymbolTable =>
// eliminated by refchecks
case ModuleDef(mods, name, impl) =>
+ case TypeTreeWithDeferredRefCheck() =>
// eliminated by erasure
case TypeDef(mods, name, tparams, rhs) =>
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 35db3c0984..dbe4a587ba 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -338,6 +338,9 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
case class Parens(args: List[Tree]) extends Tree // only used during parsing
+ /** emitted by typer, eliminated by refchecks **/
+ case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends AbsTypeTree
+
// ----- subconstructors --------------------------------------------
class ApplyToImplicitArgs(fun: Tree, args: List[Tree]) extends Apply(fun, args)
@@ -383,6 +386,7 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
def Ident(tree: Tree, name: Name): Ident
def Literal(tree: Tree, value: Constant): Literal
def TypeTree(tree: Tree): TypeTree
+ def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck
def Annotated(tree: Tree, annot: Tree, arg: Tree): Annotated
def SingletonTypeTree(tree: Tree, ref: Tree): SingletonTypeTree
def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name): SelectFromTypeTree
@@ -470,6 +474,9 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
new Literal(value).copyAttrs(tree)
def TypeTree(tree: Tree) =
new TypeTree().copyAttrs(tree)
+ def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match {
+ case dc@TypeTreeWithDeferredRefCheck() => new TypeTreeWithDeferredRefCheck()(dc.check).copyAttrs(tree)
+ }
def Annotated(tree: Tree, annot: Tree, arg: Tree) =
new Annotated(annot, arg).copyAttrs(tree)
def SingletonTypeTree(tree: Tree, ref: Tree) =
@@ -670,6 +677,10 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
case t @ TypeTree() => t
case _ => treeCopy.TypeTree(tree)
}
+ def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match {
+ case t @ TypeTreeWithDeferredRefCheck() => t
+ case _ => treeCopy.TypeTreeWithDeferredRefCheck(tree)
+ }
def Annotated(tree: Tree, annot: Tree, arg: Tree) = tree match {
case t @ Annotated(annot0, arg0)
if (annot0==annot) => t
@@ -816,6 +827,8 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
treeCopy.Literal(tree, value)
case TypeTree() =>
treeCopy.TypeTree(tree)
+ case TypeTreeWithDeferredRefCheck() =>
+ treeCopy.TypeTreeWithDeferredRefCheck(tree)
case Annotated(annot, arg) =>
treeCopy.Annotated(tree, transform(annot), transform(arg))
case SingletonTypeTree(ref) =>
@@ -878,6 +891,8 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
traverse(definition)
case Parens(ts) =>
traverseTrees(ts)
+ case TypeTreeWithDeferredRefCheck() => // TODO: should we traverse the wrapped tree?
+ // (and rewrap the result? how to update the deferred check? would need to store wrapped tree instead of returning it from check)
case _ => super.traverse(tree)
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index e28f07e840..f3149f7723 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -1820,7 +1820,7 @@ self =>
if (in.token != RPAREN) {
if (in.token == IMPLICIT) {
if (!contextBounds.isEmpty)
- syntaxError("cannot have both implicit parameters and context bounds `: ...' on type parameters", false)
+ syntaxError("cannot have both implicit parameters and context bounds `: ...' or view bounds `<% ...' on type parameters", false)
in.nextToken()
implicitmod = Flags.IMPLICIT
}
@@ -2328,7 +2328,7 @@ self =>
classContextBounds = contextBoundBuf.toList
val tstart = (in.offset::classContextBounds.map(_.pos.startOrPoint)).min
if (!classContextBounds.isEmpty && mods.hasFlag(Flags.TRAIT)) {
- syntaxError("traits cannot have type parameters with context bounds `: ...'", false)
+ syntaxError("traits cannot have type parameters with context bounds `: ...' nor view bounds `<% ...'", false)
classContextBounds = List()
}
val constrAnnots = annotations(false, true)
diff --git a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala
index 40177fad10..2429f53aa1 100644
--- a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala
+++ b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala
@@ -123,6 +123,18 @@ trait AnnotationInfos extends reflect.generic.AnnotationInfos { self: SymbolTabl
val subs = new TreeSymSubstituter(List(from), List(to))
AnnotationInfo(atp, args.map(subs(_)), assocs).setPos(pos)
}
+
+ // !!! when annotation arguments are not literal strings, but any sort of
+ // assembly of strings, there is a fair chance they will turn up here not as
+ // Literal(const) but some arbitrary AST.
+ def stringArg(index: Int): Option[String] = if(args.size > index) Some(args(index) match {
+ case Literal(const) => const.stringValue
+ case x => x.toString // should not be necessary, but better than silently ignoring an issue
+ }) else None
+
+ def intArg(index: Int): Option[Int] = if(args.size > index) Some(args(index)) collect {
+ case Literal(Constant(x: Int)) => x
+ } else None
}
object AnnotationInfo extends AnnotationInfoExtractor
diff --git a/src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala b/src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala
index c83138e9bc..c230533765 100644
--- a/src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala
+++ b/src/compiler/scala/tools/nsc/symtab/BaseTypeSeqs.scala
@@ -6,8 +6,7 @@ package scala.tools.nsc
package symtab
// todo implement in terms of BitSet
-import scala.collection.mutable.ListBuffer
-import scala.collection.immutable.Map
+import scala.collection.mutable.{ListBuffer, BitSet}
import math.max
import util.Statistics._
@@ -32,45 +31,48 @@ trait BaseTypeSeqs {
class BaseTypeSeq(parents: List[Type], elems: Array[Type]) {
self =>
-
incCounter(baseTypeSeqCount)
incCounter(baseTypeSeqLenTotal, elems.length)
/** The number of types in the sequence */
def length: Int = elems.length
- var pending: Map[Int, Type] = Map()
+ // #3676 shows why we can't store NoType in elems to mark cycles
+ // (while NoType is in there to indicate a cycle in this BTS, during the execution of
+ // the mergePrefixAndArgs below, the elems get copied without the pending map,
+ // so that NoType's are seen instead of the original type --> spurious compile error)
+ val pending = new BitSet(length)
/** The type at i'th position in this sequence; lazy types are returned evaluated. */
- def apply(i: Int): Type = elems(i) match {
- case NoType =>
- pending = Map()
- elems(i) = AnyClass.tpe
+ def apply(i: Int): Type =
+ if(pending contains i) {
+ pending.clear()
throw CyclicInheritance
- case rtp @ RefinedType(variants, decls) =>
- // can't assert decls.isEmpty; see t0764
- //if (!decls.isEmpty) assert(false, "computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j))
- //Console.println("compute closure of "+this+" => glb("+variants+")")
- pending += (i -> rtp)
- elems(i) = NoType
- try {
- mergePrefixAndArgs(variants, -1, lubDepth(variants)) match {
- case Some(tp0) =>
- pending -= i
- elems(i) = tp0
- tp0
- case None =>
- typeError(
- "no common type instance of base types "+(variants mkString ", and ")+" exists.")
- }
- } catch {
- case CyclicInheritance =>
- typeError(
- "computing the common type instance of base types "+(variants mkString ", and ")+" leads to a cycle.")
+ } else
+ elems(i) match {
+ case rtp @ RefinedType(variants, decls) =>
+ // can't assert decls.isEmpty; see t0764
+ //if (!decls.isEmpty) assert(false, "computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j))
+ //Console.println("compute closure of "+this+" => glb("+variants+")")
+ pending += i
+ try {
+ mergePrefixAndArgs(variants, -1, lubDepth(variants)) match {
+ case Some(tp0) =>
+ pending(i) = false
+ elems(i) = tp0
+ tp0
+ case None =>
+ typeError(
+ "no common type instance of base types "+(variants mkString ", and ")+" exists.")
+ }
+ } catch {
+ case CyclicInheritance =>
+ typeError(
+ "computing the common type instance of base types "+(variants mkString ", and ")+" leads to a cycle.")
+ }
+ case tp =>
+ tp
}
- case tp =>
- tp
- }
def rawElem(i: Int) = elems(i)
@@ -78,17 +80,9 @@ trait BaseTypeSeqs {
* no evaluation needed.
*/
def typeSymbol(i: Int): Symbol = {
- def tsym(tp: Type) = tp match {
- case RefinedType(v :: vs, _) => v.typeSymbol
- case _ => tp.typeSymbol
- }
elems(i) match {
- case NoType =>
- pending get i match {
- case Some(tp) => tsym(tp)
- case _ => NoType.typeSymbol
- }
- case tp => tsym(tp)
+ case RefinedType(v :: vs, _) => v.typeSymbol
+ case tp => tp.typeSymbol
}
}
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 3e3b32ac01..92d8b213c1 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -120,6 +120,7 @@ trait Definitions extends reflect.generic.StandardDefinitions {
lazy val TailrecClass = getClass("scala.annotation.tailrec")
lazy val SwitchClass = getClass("scala.annotation.switch")
lazy val ElidableMethodClass = getClass("scala.annotation.elidable")
+ lazy val ImplicitNotFoundClass = getClass("scala.annotation.implicitNotFound")
lazy val FieldTargetClass = getClass("scala.annotation.target.field")
lazy val GetterTargetClass = getClass("scala.annotation.target.getter")
lazy val SetterTargetClass = getClass("scala.annotation.target.setter")
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index 10a7bbf705..e16612ae49 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -132,14 +132,6 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
def getAnnotation(cls: Symbol): Option[AnnotationInfo] =
annotations find (_.atp.typeSymbol == cls)
- /** Finds the requested annotation and returns Some(Tree) containing
- * the argument at position 'index', or None if either the annotation
- * or the index does not exist.
- */
- private def getAnnotationArg(cls: Symbol, index: Int) =
- for (AnnotationInfo(_, args, _) <- getAnnotation(cls) ; if args.size > index) yield
- args(index)
-
/** Remove all annotations matching the given class. */
def removeAnnotation(cls: Symbol): Unit =
setAnnotations(annotations filterNot (_.atp.typeSymbol == cls))
@@ -461,18 +453,16 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
}
def isDeprecated = hasAnnotation(DeprecatedAttr)
- def deprecationMessage = getAnnotationArg(DeprecatedAttr, 0) collect { case Literal(const) => const.stringValue }
+ def deprecationMessage = getAnnotation(DeprecatedAttr) flatMap { _.stringArg(0) }
// !!! when annotation arguments are not literal strings, but any sort of
// assembly of strings, there is a fair chance they will turn up here not as
// Literal(const) but some arbitrary AST. However nothing in the compiler
// prevents someone from writing a @migration annotation with a calculated
// string. So this needs attention. For now the fact that migration is
// private[scala] ought to provide enough protection.
- def migrationMessage = getAnnotationArg(MigrationAnnotationClass, 2) collect {
- case Literal(const) => const.stringValue
- case x => x.toString // should not be necessary, but better than silently ignoring an issue
- }
- def elisionLevel = getAnnotationArg(ElidableMethodClass, 0) collect { case Literal(Constant(x: Int)) => x }
+ def migrationMessage = getAnnotation(MigrationAnnotationClass) flatMap { _.stringArg(2) }
+ def elisionLevel = getAnnotation(ElidableMethodClass) flatMap { _.intArg(0) }
+ def implicitNotFoundMsg = getAnnotation(ImplicitNotFoundClass) flatMap { _.stringArg(0) }
/** Does this symbol denote a wrapper object of the interpreter or its class? */
final def isInterpreterWrapper =
@@ -776,6 +766,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
assert(phaseId(infos.validFrom) <= phase.id)
if (phaseId(infos.validFrom) == phase.id) infos = infos.prev
infos = TypeHistory(currentPeriod, info, infos)
+ validTo = if (info.isComplete) currentPeriod else NoPeriod
this
}
@@ -1073,6 +1064,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
/** A clone of this symbol, but with given owner */
final def cloneSymbol(owner: Symbol): Symbol = {
val newSym = cloneSymbolImpl(owner)
+ // newSym.privateWithin = privateWithin // ?
newSym.setInfo(info.cloneInfo(newSym))
.setFlag(this.rawflags).setAnnotations(this.annotations)
}
@@ -1967,7 +1959,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
newTypeName(rawname+"$trait") // (part of DEVIRTUALIZE)
} else if (phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass) {
if (flatname == nme.EMPTY) {
- assert(rawowner.isClass, "fatal: %s has owner %s, but a class owner is required".format(rawname, rawowner))
+ assert(rawowner.isClass, "fatal: %s has owner %s, but a class owner is required".format(rawname+idString, rawowner))
flatname = newTypeName(compactify(rawowner.name.toString() + "$" + rawname))
}
flatname
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 2eca101aab..7aed4cd648 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -137,7 +137,7 @@ trait Types extends reflect.generic.Types { self: SymbolTable =>
* It makes use of the fact that these two operations depend only on the parents,
* not on the refinement.
*/
- var intersectionWitness = new WeakHashMap[List[Type], Type]
+ val intersectionWitness = new WeakHashMap[List[Type], Type]
private object gen extends {
val global : Types.this.type = Types.this
@@ -394,9 +394,10 @@ trait Types extends reflect.generic.Types { self: SymbolTable =>
/** Replace formal type parameter symbols with actual type arguments.
*
- * Amounts to substitution except for higher-kinded types. (See overridden method in TypeRef) -- @M (contact adriaan.moors at cs.kuleuven.be)
+ * Amounts to substitution except for higher-kinded types. (See overridden method in TypeRef) -- @M
*/
- def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type = this.subst(formals, actuals)
+ def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type =
+ if(formals.length == actuals.length) this.subst(formals, actuals) else ErrorType
/** If this type is an existential, turn all existentially bound variables to type skolems.
* @param owner The owner of the created type skolems
@@ -1327,7 +1328,7 @@ trait Types extends reflect.generic.Types { self: SymbolTable =>
* to take the intersection of their bounds
*/
override def normalize = {
- if (isHigherKinded)
+ if (isHigherKinded) {
PolyType(
typeParams,
RefinedType(
@@ -1337,6 +1338,7 @@ trait Types extends reflect.generic.Types { self: SymbolTable =>
},
decls,
typeSymbol))
+ }
else super.normalize
}
@@ -1705,8 +1707,9 @@ A type's typeSymbol should never be inspected directly.
if (substTps.length == typeParams.length)
typeRef(pre, sym, actuals)
- else // partial application (needed in infer when bunching type arguments from classes and methods together)
+ else if(formals.length == actuals.length) // partial application (needed in infer when bunching type arguments from classes and methods together)
typeRef(pre, sym, dummyArgs).subst(formals, actuals)
+ else ErrorType
}
else
super.instantiateTypeParams(formals, actuals)
@@ -1725,21 +1728,15 @@ A type's typeSymbol should never be inspected directly.
if (sym == clazz && !args.isEmpty) args.head else this
def normalize0: Type =
- if (sym.isAliasType) { // beta-reduce
- if (sym.info.typeParams.length == args.length || !isHigherKinded) {
- /* !isHigherKinded && sym.info.typeParams.length != args.length only happens when compiling e.g.,
- `val x: Class' with -Xgenerics, while `type Class = java.lang.Class' had already been compiled without -Xgenerics */
- val xform = transform(sym.info.resultType)
- assert(xform ne this, this)
- xform.normalize // cycles have been checked in typeRef
- } else { // should rarely happen, if at all
- PolyType(sym.info.typeParams, transform(sym.info.resultType).normalize) // eta-expand -- for regularity, go through sym.info for typeParams
- // @M TODO: should not use PolyType, as that's the type of a polymorphic value -- we really want a type *function*
- }
- } else if (isHigherKinded) {
+ if (isHigherKinded) {
// @M TODO: should not use PolyType, as that's the type of a polymorphic value -- we really want a type *function*
// @M: initialize (by sym.info call) needed (see test/files/pos/ticket0137.scala)
PolyType(sym.info.typeParams, typeRef(pre, sym, dummyArgs)) // must go through sym.info for typeParams
+ } else if (sym.isAliasType) { // beta-reduce
+ if(sym.info.typeParams.length == args.length) // don't do partial application
+ transform(sym.info.resultType).normalize // cycles have been checked in typeRef
+ else
+ ErrorType
} else if (sym.isRefinementClass) {
sym.info.normalize // @MO to AM: OK?
//@M I think this is okay, but changeset 12414 (which fixed #1241) re-introduced another bug (#2208)
@@ -2247,14 +2244,17 @@ A type's typeSymbol should never be inspected directly.
if(params.isEmpty) { // type var has kind *
addBound(tp)
true
- } else // higher-kinded type var with same arity as tp
- (typeArgs.length == tp.typeArgs.length) && {
- // register type constructor (the type without its type arguments) as bound
- addBound(tp.typeConstructor)
- // check subtyping of higher-order type vars
- // use variances as defined in the type parameter that we're trying to infer (the result is sanity-checked later)
- checkArgs(tp.typeArgs, typeArgs, params)
- }
+ } else { // higher-kinded type var with same arity as tp
+ def unifyHK(tp: Type) =
+ (typeArgs.length == tp.typeArgs.length) && {
+ // register type constructor (the type without its type arguments) as bound
+ addBound(tp.typeConstructor)
+ // check subtyping of higher-order type vars
+ // use variances as defined in the type parameter that we're trying to infer (the result is sanity-checked later)
+ checkArgs(tp.typeArgs, typeArgs, params)
+ }
+ unifyHK(tp) || unifyHK(tp.dealias)
+ }
}
}
@@ -4244,9 +4244,8 @@ A type's typeSymbol should never be inspected directly.
// this optimisation holds because inlining cloneSymbols in `val tpsFresh = cloneSymbols(tparams1)` gives:
// val tpsFresh = tparams1 map (_.cloneSymbol)
// for (tpFresh <- tpsFresh) tpFresh.setInfo(tpFresh.info.substSym(tparams1, tpsFresh))
- }
}
-
+ }
case (_, _) => false // @assume !tp1.isHigherKinded || !tp2.isHigherKinded
// --> thus, cannot be subtypes (Any/Nothing has already been checked)
}))
@@ -4482,7 +4481,12 @@ A type's typeSymbol should never be inspected directly.
val info2 = tp2.memberInfo(sym2).substThis(tp2.typeSymbol, tp1)
//System.out.println("specializes "+tp1+"."+sym1+":"+info1+sym1.locationString+" AND "+tp2+"."+sym2+":"+info2)//DEBUG
sym2.isTerm && (info1 <:< info2) /*&& (!sym2.isStable || sym1.isStable) */ ||
- sym2.isAbstractType && info2.bounds.containsType(tp1.memberType(sym1)) ||
+ sym2.isAbstractType && {
+ val memberTp1 = tp1.memberType(sym1)
+ // println("kinds conform? "+(memberTp1, tp1, sym2, kindsConform(List(sym2), List(memberTp1), tp2, sym2.owner)))
+ info2.bounds.containsType(memberTp1) &&
+ kindsConform(List(sym2), List(memberTp1), tp1, sym1.owner)
+ } ||
sym2.isAliasType && tp2.memberType(sym2).substThis(tp2.typeSymbol, tp1) =:= tp1.memberType(sym1) //@MAT ok
}
@@ -5086,37 +5090,41 @@ A type's typeSymbol should never be inspected directly.
case List(tp) =>
Some(tp)
case TypeRef(_, sym, _) :: rest =>
- val pres = tps map (_.prefix)
+ val pres = tps map (_.prefix) // prefix normalizes automatically
val pre = if (variance == 1) lub(pres, depth) else glb(pres, depth)
- val argss = tps map (_.typeArgs)
+ val argss = tps map (_.normalize.typeArgs) // symbol equality (of the tp in tps) was checked using typeSymbol, which normalizes, so should normalize before retrieving arguments
val capturedParams = new ListBuffer[Symbol]
- val args = (sym.typeParams, argss.transpose).zipped map {
- (tparam, as) =>
- if (depth == 0)
- if (tparam.variance == variance) AnyClass.tpe
- else if (tparam.variance == -variance) NothingClass.tpe
- else NoType
- else
- if (tparam.variance == variance) lub(as, decr(depth))
- else if (tparam.variance == -variance) glb(as, decr(depth))
- else {
- val l = lub(as, decr(depth))
- val g = glb(as, decr(depth))
- if (l <:< g) l
- else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we
- // just err on the conservative side, i.e. with a bound that is too high.
- // if(!(tparam.info.bounds contains tparam)){ //@M can't deal with f-bounds, see #2251
- val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l)
- capturedParams += qvar
- qvar.tpe
- }
- }
- }
try {
+ val args = (sym.typeParams, argss.transpose).zipped map {
+ (tparam, as) =>
+ if (depth == 0)
+ if (tparam.variance == variance) AnyClass.tpe
+ else if (tparam.variance == -variance) NothingClass.tpe
+ else NoType
+ else
+ if (tparam.variance == variance) lub(as, decr(depth))
+ else if (tparam.variance == -variance) glb(as, decr(depth))
+ else {
+ val l = lub(as, decr(depth))
+ val g = glb(as, decr(depth))
+ if (l <:< g) l
+ else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we
+ // just err on the conservative side, i.e. with a bound that is too high.
+ // if(!(tparam.info.bounds contains tparam)){ //@M can't deal with f-bounds, see #2251
+ val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l)
+ capturedParams += qvar
+ qvar.tpe
+ }
+ }
+ }
if (args contains NoType) None
else Some(existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args)))
} catch {
case ex: MalformedType => None
+ case ex: IndexOutOfBoundsException => // transpose freaked out because of irregular argss
+ // catching just in case (shouldn't happen, but also doesn't cost us)
+ if (settings.debug.value) log("transposed irregular matrix!?"+ (tps, argss))
+ None
}
case SingleType(_, sym) :: rest =>
val pres = tps map (_.prefix)
@@ -5184,6 +5192,113 @@ A type's typeSymbol should never be inspected directly.
throw new NoCommonType(tps)
}
+
+// TODO: this desperately needs to be cleaned up
+// plan: split into kind inference and subkinding
+// every Type has a (cached) Kind
+ def kindsConform(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol): Boolean = checkKindBounds0(tparams, targs, pre, owner, false).isEmpty
+
+ /** Check well-kindedness of type application (assumes arities are already checked) -- @M
+ *
+ * This check is also performed when abstract type members become concrete (aka a "type alias") -- then tparams.length==1
+ * (checked one type member at a time -- in that case, prefix is the name of the type alias)
+ *
+ * Type application is just like value application: it's "contravariant" in the sense that
+ * the type parameters of the supplied type arguments must conform to the type parameters of
+ * the required type parameters:
+ * - their bounds must be less strict
+ * - variances must match (here, variances are absolute, the variance of a type parameter does not influence the variance of its higher-order parameters)
+ * - @M TODO: are these conditions correct,sufficient&necessary?
+ *
+ * e.g. class Iterable[t, m[+x <: t]] --> the application Iterable[Int, List] is okay, since
+ * List's type parameter is also covariant and its bounds are weaker than <: Int
+ */
+ def checkKindBounds0(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol, explainErrors: Boolean): List[(Type, Symbol, List[(Symbol, Symbol)], List[(Symbol, Symbol)], List[(Symbol, Symbol)])] = {
+ var error = false
+
+ def transform(tp: Type, clazz: Symbol): Type = tp.asSeenFrom(pre, clazz) // instantiate type params that come from outside the abstract type we're currently checking
+ def transformedBounds(p: Symbol, o: Symbol) = transform(p.info.instantiateTypeParams(tparams, targs).bounds, o)
+
+ /** Check whether <arg>sym1</arg>'s variance conforms to <arg>sym2</arg>'s variance
+ *
+ * If <arg>sym2</arg> is invariant, <arg>sym1</arg>'s variance is irrelevant. Otherwise they must be equal.
+ */
+ def variancesMatch(sym1: Symbol, sym2: Symbol): Boolean = (sym2.variance==0 || sym1.variance==sym2.variance)
+
+ // check that the type parameters <arg>hkargs</arg> to a higher-kinded type conform to the expected params <arg>hkparams</arg>
+ def checkKindBoundsHK(hkargs: List[Symbol], arg: Symbol, param: Symbol, paramowner: Symbol, underHKParams: List[Symbol], withHKArgs: List[Symbol]): (List[(Symbol, Symbol)], List[(Symbol, Symbol)], List[(Symbol, Symbol)]) = {
+ def bindHKParams(tp: Type) = tp.substSym(underHKParams, withHKArgs)
+ // @M sometimes hkargs != arg.typeParams, the symbol and the type may have very different type parameters
+ val hkparams = param.typeParams
+
+ if(settings.debug.value) {
+ println("checkKindBoundsHK expected: "+ param +" with params "+ hkparams +" by definition in "+ paramowner)
+ println("checkKindBoundsHK supplied: "+ arg +" with params "+ hkargs +" from "+ owner)
+ println("checkKindBoundsHK under params: "+ underHKParams +" with args "+ withHKArgs)
+ }
+
+ if(hkargs.length != hkparams.length) {
+ if(arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded
+ else {error = true; (List((arg, param)), Nil, Nil)} // shortcut: always set error, whether explainTypesOrNot
+ } else {
+ val _arityMismatches = if(explainErrors) new ListBuffer[(Symbol, Symbol)] else null
+ val _varianceMismatches = if(explainErrors) new ListBuffer[(Symbol, Symbol)] else null
+ val _stricterBounds = if(explainErrors)new ListBuffer[(Symbol, Symbol)] else null
+ def varianceMismatch(a: Symbol, p: Symbol) { if(explainErrors) _varianceMismatches += ((a, p)) else error = true}
+ def stricterBound(a: Symbol, p: Symbol) { if(explainErrors) _stricterBounds += ((a, p)) else error = true }
+ def arityMismatches(as: Iterable[(Symbol, Symbol)]) { if(explainErrors) _arityMismatches ++= as }
+ def varianceMismatches(as: Iterable[(Symbol, Symbol)]) { if(explainErrors) _varianceMismatches ++= as }
+ def stricterBounds(as: Iterable[(Symbol, Symbol)]) { if(explainErrors) _stricterBounds ++= as }
+
+ for ((hkarg, hkparam) <- hkargs zip hkparams) {
+ if (hkparam.typeParams.isEmpty) { // base-case: kind *
+ if (!variancesMatch(hkarg, hkparam))
+ varianceMismatch(hkarg, hkparam)
+
+ // instantiateTypeParams(tparams, targs) --> higher-order bounds may contain references to type arguments
+ // substSym(hkparams, hkargs) --> these types are going to be compared as types of kind *
+ // --> their arguments use different symbols, but are conceptually the same
+ // (could also replace the types by polytypes, but can't just strip the symbols, as ordering is lost then)
+ if (!(bindHKParams(transformedBounds(hkparam, paramowner)) <:< transform(hkarg.info.bounds, owner)))
+ stricterBound(hkarg, hkparam)
+
+ if(settings.debug.value) {
+ println("checkKindBoundsHK base case: "+ hkparam +" declared bounds: "+ transformedBounds(hkparam, paramowner) +" after instantiating earlier hkparams: "+ bindHKParams(transformedBounds(hkparam, paramowner)))
+ println("checkKindBoundsHK base case: "+ hkarg +" has bounds: "+ transform(hkarg.info.bounds, owner))
+ }
+ } else {
+ if(settings.debug.value) println("checkKindBoundsHK recursing to compare params of "+ hkparam +" with "+ hkarg)
+ val (am, vm, sb) = checkKindBoundsHK(hkarg.typeParams, hkarg, hkparam, paramowner, underHKParams ++ hkparam.typeParams, withHKArgs ++ hkarg.typeParams)
+ arityMismatches(am)
+ varianceMismatches(vm)
+ stricterBounds(sb)
+ }
+ if(!explainErrors && error) return (Nil, Nil, Nil) // stop as soon as we encountered an error
+ }
+ if(!explainErrors) (Nil, Nil, Nil)
+ else (_arityMismatches.toList, _varianceMismatches.toList, _stricterBounds.toList)
+ }
+ }
+
+ val errors = new ListBuffer[(Type, Symbol, List[(Symbol, Symbol)], List[(Symbol, Symbol)], List[(Symbol, Symbol)])]
+ (tparams zip targs).foreach{ case (tparam, targ) if (targ.isHigherKinded || !tparam.typeParams.isEmpty) =>
+ // @M must use the typeParams of the type targ, not the typeParams of the symbol of targ!!
+ val tparamsHO = targ.typeParams
+
+ val (arityMismatches, varianceMismatches, stricterBounds) =
+ checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO) // NOTE: *not* targ.typeSymbol, which normalizes
+
+ if(!explainErrors) {if(error) return List((NoType, NoSymbol, Nil, Nil, Nil))}
+ else if (arityMismatches.nonEmpty || varianceMismatches.nonEmpty || stricterBounds.nonEmpty) {
+ errors += ((targ, tparam, arityMismatches, varianceMismatches, stricterBounds))
+ }
+ // case (tparam, targ) => println("no check: "+(tparam, targ, tparam.typeParams.isEmpty))
+ case _ =>
+ }
+
+ errors.toList
+ }
+
// Errors and Diagnostics -----------------------------------------------------
/** A throwable signalling a type error */
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index f33be12bd4..52206f01de 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -228,7 +228,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
/** The Java signature of type 'info', for symbol sym. The symbol is used to give the right return
* type for constructors.
*/
- def javaSig(sym: Symbol, info: Type): Option[String] = atPhase(currentRun.erasurePhase) {
+ def javaSig(sym0: Symbol, info: Type): Option[String] = atPhase(currentRun.erasurePhase) {
def jsig(tp: Type): String = jsig2(false, List(), tp)
@@ -260,7 +260,11 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
"."+sym.name
if (sym == ArrayClass)
ARRAY_TAG.toString+(args map jsig).mkString
- else if (sym.isTypeParameterOrSkolem && !sym.owner.isTypeParameterOrSkolem /*not a higher-order type parameter, as these are suppressed*/)
+ else if (sym.isTypeParameterOrSkolem &&
+ // only refer to type params that will actually make it into the sig, this excludes:
+ !sym.owner.isTypeParameterOrSkolem && // higher-order type parameters (!sym.owner.isTypeParameterOrSkolem), and parameters of methods
+ (!sym0.isClass || sym.owner.isClass) // if we're generating the sig for a class, type params must be owned by a class (not a method -- #3249)
+ )
TVAR_TAG.toString+sym.name+";"
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass)
jsig(ObjectClass.tpe)
@@ -302,7 +306,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
(if (toplevel) "<"+(tparams map paramSig).mkString+">" else "")+jsig(restpe)
case MethodType(params, restpe) =>
"("+(params map (_.tpe) map jsig).mkString+")"+
- (if (restpe.typeSymbol == UnitClass || sym.isConstructor) VOID_TAG.toString else jsig(restpe))
+ (if (restpe.typeSymbol == UnitClass || sym0.isConstructor) VOID_TAG.toString else jsig(restpe))
case RefinedType(parents, decls) if (!parents.isEmpty) =>
jsig(parents.head)
case ClassInfoType(parents, _, _) =>
@@ -310,7 +314,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
case AnnotatedType(_, atp, _) =>
jsig(atp)
case BoundedWildcardType(bounds) =>
- println("something's wrong: "+sym+":"+sym.tpe+" has a bounded wildcard type")
+ println("something's wrong: "+sym0+":"+sym0.tpe+" has a bounded wildcard type")
jsig(bounds.hi)
case _ =>
val etp = erasure(tp)
@@ -320,7 +324,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
}
if (needsJavaSig(info)) {
try {
- //println("Java sig of "+sym+" is "+jsig2(true, List(), sym.info))//DEBUG
+ //println("Java sig of "+sym0+" is "+jsig2(true, List(), sym0.info))//DEBUG
Some(jsig2(true, List(), info))
} catch {
case ex: UnknownSig => None
@@ -502,6 +506,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
}
/** Generate a synthetic cast operation from <code>tree.tpe</code> to <code>pt</code>.
+ * @pre pt eq pt.normalize
*/
private def cast(tree: Tree, pt: Type): Tree =
tree AS_ATTR pt
@@ -512,7 +517,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
/** Adapt <code>tree</code> to expected type <code>pt</code>.
*
* @param tree the given tree
- * @param pt the expected type.
+ * @param pt the expected type
* @return the adapted tree
*/
private def adaptToType(tree: Tree, pt: Type): Tree = {
@@ -928,154 +933,154 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
* </ul>
*/
private val preTransformer = new Transformer {
- override def transform(tree: Tree): Tree = {
- if (tree.symbol == ArrayClass && !tree.isType) return tree // !!! needed?
- val tree1 = tree match {
- case ClassDef(mods, name, tparams, impl) =>
- if (settings.debug.value)
- log("defs of " + tree.symbol + " = " + tree.symbol.info.decls)
- treeCopy.ClassDef(tree, mods, name, List(), impl)
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
- treeCopy.DefDef(tree, mods, name, List(), vparamss, tpt, rhs)
- case TypeDef(_, _, _, _) =>
- EmptyTree
- case Apply(instanceOf @ TypeApply(fun @ Select(qual, name), args @ List(arg)), List()) // !!! todo: simplify by having GenericArray also extract trees
- if ((fun.symbol == Any_isInstanceOf || fun.symbol == Object_isInstanceOf) &&
- unboundedGenericArrayLevel(arg.tpe) > 0) =>
- val level = unboundedGenericArrayLevel(arg.tpe)
- def isArrayTest(arg: Tree) =
- gen.mkRuntimeCall("isArray", List(arg, Literal(Constant(level))))
- typedPos(tree.pos) {
- if (level == 1) isArrayTest(qual)
- else
- gen.evalOnce(qual, currentOwner, unit) { qual1 =>
- gen.mkAnd(
- Apply(TypeApply(Select(qual1(), fun.symbol),
- List(TypeTree(erasure(arg.tpe)))),
- List()),
- isArrayTest(qual1()))
- }
- }
- case TypeApply(fun, args) if (fun.symbol.owner != AnyClass &&
- fun.symbol != Object_asInstanceOf &&
- fun.symbol != Object_isInstanceOf) =>
- // leave all other type tests/type casts, remove all other type applications
- fun
- case Apply(fn @ Select(qual, name), args) if (fn.symbol.owner == ArrayClass) =>
- if (unboundedGenericArrayLevel(qual.tpe.widen) == 1)
- // convert calls to apply/update/length on generic arrays to
- // calls of ScalaRunTime.array_xxx method calls
- typedPos(tree.pos) { gen.mkRuntimeCall("array_"+name, qual :: args) }
+ def preErase(tree: Tree): Tree = tree match {
+ case ClassDef(mods, name, tparams, impl) =>
+ if (settings.debug.value)
+ log("defs of " + tree.symbol + " = " + tree.symbol.info.decls)
+ treeCopy.ClassDef(tree, mods, name, List(), impl)
+ case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ treeCopy.DefDef(tree, mods, name, List(), vparamss, tpt, rhs)
+ case TypeDef(_, _, _, _) =>
+ EmptyTree
+ case Apply(instanceOf @ TypeApply(fun @ Select(qual, name), args @ List(arg)), List()) // !!! todo: simplify by having GenericArray also extract trees
+ if ((fun.symbol == Any_isInstanceOf || fun.symbol == Object_isInstanceOf) &&
+ unboundedGenericArrayLevel(arg.tpe) > 0) =>
+ val level = unboundedGenericArrayLevel(arg.tpe)
+ def isArrayTest(arg: Tree) =
+ gen.mkRuntimeCall("isArray", List(arg, Literal(Constant(level))))
+ typedPos(tree.pos) {
+ if (level == 1) isArrayTest(qual)
else
- // store exact array erasure in map to be retrieved later when we might
- // need to do the cast in adaptMember
- treeCopy.Apply(
- tree,
- SelectFromArray(qual, name, erasure(qual.tpe)).copyAttrs(fn),
- args)
+ gen.evalOnce(qual, currentOwner, unit) { qual1 =>
+ gen.mkAnd(
+ Apply(TypeApply(Select(qual1(), fun.symbol),
+ List(TypeTree(erasure(arg.tpe)))),
+ List()),
+ isArrayTest(qual1()))
+ }
+ }
+ case TypeApply(fun, args) if (fun.symbol.owner != AnyClass &&
+ fun.symbol != Object_asInstanceOf &&
+ fun.symbol != Object_isInstanceOf) =>
+ // leave all other type tests/type casts, remove all other type applications
+ preErase(fun)
+ case Apply(fn @ Select(qual, name), args) if (fn.symbol.owner == ArrayClass) =>
+ if (unboundedGenericArrayLevel(qual.tpe.widen) == 1)
+ // convert calls to apply/update/length on generic arrays to
+ // calls of ScalaRunTime.array_xxx method calls
+ typedPos(tree.pos) { gen.mkRuntimeCall("array_"+name, qual :: args) }
+ else
+ // store exact array erasure in map to be retrieved later when we might
+ // need to do the cast in adaptMember
+ treeCopy.Apply(
+ tree,
+ SelectFromArray(qual, name, erasure(qual.tpe)).copyAttrs(fn),
+ args)
- case Apply(fn @ Select(qual, _), Nil) if (fn.symbol == Any_## || fn.symbol == Object_##) =>
- Apply(gen.mkAttributedRef(scalaRuntimeHash), List(qual))
+ case Apply(fn @ Select(qual, _), Nil) if (fn.symbol == Any_## || fn.symbol == Object_##) =>
+ Apply(gen.mkAttributedRef(scalaRuntimeHash), List(qual))
- case Apply(fn, args) =>
- if (fn.symbol == Any_asInstanceOf)
- fn match {
- case TypeApply(Select(qual, _), List(targ)) =>
- if (qual.tpe <:< targ.tpe) {
- atPos(tree.pos) { Typed(qual, TypeTree(targ.tpe)) }
- } else if (isNumericValueClass(qual.tpe.typeSymbol) &&
- isNumericValueClass(targ.tpe.typeSymbol)) {
- // convert numeric type casts
- val cname = newTermName("to" + targ.tpe.typeSymbol.name)
- val csym = qual.tpe.member(cname)
- assert(csym != NoSymbol)
- atPos(tree.pos) { Apply(Select(qual, csym), List()) }
- } else
- tree
- }
- // todo: also handle the case where the singleton type is buried in a compound
- else if (fn.symbol == Any_isInstanceOf)
- fn match {
- case TypeApply(sel @ Select(qual, name), List(targ)) =>
- def mkIsInstanceOf(q: () => Tree)(tp: Type): Tree =
- Apply(
- TypeApply(
- Select(q(), Object_isInstanceOf) setPos sel.pos,
- List(TypeTree(tp) setPos targ.pos)) setPos fn.pos,
- List()) setPos tree.pos
- targ.tpe match {
- case SingleType(_, _) | ThisType(_) | SuperType(_, _) =>
- val cmpOp = if (targ.tpe <:< AnyValClass.tpe) Any_equals else Object_eq
+ case Apply(fn, args) =>
+ if (fn.symbol == Any_asInstanceOf)
+ fn match {
+ case TypeApply(Select(qual, _), List(targ)) =>
+ if (qual.tpe <:< targ.tpe) {
+ atPos(tree.pos) { Typed(qual, TypeTree(targ.tpe)) }
+ } else if (isNumericValueClass(qual.tpe.typeSymbol) &&
+ isNumericValueClass(targ.tpe.typeSymbol)) {
+ // convert numeric type casts
+ val cname = newTermName("to" + targ.tpe.typeSymbol.name)
+ val csym = qual.tpe.member(cname)
+ assert(csym != NoSymbol)
+ atPos(tree.pos) { Apply(Select(qual, csym), List()) }
+ } else
+ tree
+ }
+ // todo: also handle the case where the singleton type is buried in a compound
+ else if (fn.symbol == Any_isInstanceOf)
+ fn match {
+ case TypeApply(sel @ Select(qual, name), List(targ)) =>
+ def mkIsInstanceOf(q: () => Tree)(tp: Type): Tree =
+ Apply(
+ TypeApply(
+ Select(q(), Object_isInstanceOf) setPos sel.pos,
+ List(TypeTree(tp) setPos targ.pos)) setPos fn.pos,
+ List()) setPos tree.pos
+ targ.tpe match {
+ case SingleType(_, _) | ThisType(_) | SuperType(_, _) =>
+ val cmpOp = if (targ.tpe <:< AnyValClass.tpe) Any_equals else Object_eq
+ atPos(tree.pos) {
+ Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe)))
+ }
+ case RefinedType(parents, decls) if (parents.length >= 2) =>
+ gen.evalOnce(qual, currentOwner, unit) { q =>
atPos(tree.pos) {
- Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe)))
- }
- case RefinedType(parents, decls) if (parents.length >= 2) =>
- gen.evalOnce(qual, currentOwner, unit) { q =>
- atPos(tree.pos) {
- parents map mkIsInstanceOf(q) reduceRight gen.mkAnd
- }
+ parents map mkIsInstanceOf(q) reduceRight gen.mkAnd
}
- case _ =>
- tree
- }
- case _ => tree
- }
- else {
- def doDynamic(fn: Tree, qual: Tree): Tree = {
- if (fn.symbol.owner.isRefinementClass && fn.symbol.allOverriddenSymbols.isEmpty)
- ApplyDynamic(qual, args) setSymbol fn.symbol setPos tree.pos
- else tree
- }
- fn match {
- case Select(qual, _) => doDynamic(fn, qual)
- case TypeApply(fni@Select(qual, _), _) => doDynamic(fni, qual)// type parameters are irrelevant in case of dynamic call
- case _ =>
- tree
- }
+ }
+ case _ =>
+ tree
+ }
+ case _ => tree
}
-
- case Select(_, _) =>
- if (tree.symbol.owner.isRefinementClass) {
- val overridden = tree.symbol.allOverriddenSymbols
- assert(!overridden.isEmpty, tree.symbol)
- tree.symbol = overridden.head
+ else {
+ def doDynamic(fn: Tree, qual: Tree): Tree = {
+ if (fn.symbol.owner.isRefinementClass && fn.symbol.allOverriddenSymbols.isEmpty)
+ ApplyDynamic(qual, args) setSymbol fn.symbol setPos tree.pos
+ else tree
}
- tree
+ fn match {
+ case Select(qual, _) => doDynamic(fn, qual)
+ case TypeApply(fni@Select(qual, _), _) => doDynamic(fni, qual)// type parameters are irrelevant in case of dynamic call
+ case _ =>
+ tree
+ }
+ }
- case Template(parents, self, body) =>
- assert(!currentOwner.isImplClass)
- //Console.println("checking no dble defs " + tree)//DEBUG
- checkNoDoubleDefs(tree.symbol.owner)
- treeCopy.Template(tree, parents, emptyValDef, addBridges(body, currentOwner))
+ case Select(_, _) =>
+ // println("preXform: "+ (tree, tree.symbol, tree.symbol.owner, tree.symbol.owner.isRefinementClass))
+ if (tree.symbol.owner.isRefinementClass) {
+ val overridden = tree.symbol.allOverriddenSymbols
+ assert(!overridden.isEmpty, tree.symbol)
+ tree.symbol = overridden.head
+ }
+ tree
- case Match(selector, cases) =>
- Match(Typed(selector, TypeTree(selector.tpe)), cases)
+ case Template(parents, self, body) =>
+ assert(!currentOwner.isImplClass)
+ //Console.println("checking no dble defs " + tree)//DEBUG
+ checkNoDoubleDefs(tree.symbol.owner)
+ treeCopy.Template(tree, parents, emptyValDef, addBridges(body, currentOwner))
- case Literal(ct) if ct.tag == ClassTag
- && ct.typeValue.typeSymbol != definitions.UnitClass =>
- treeCopy.Literal(tree, Constant(erasure(ct.typeValue)))
+ case Match(selector, cases) =>
+ Match(Typed(selector, TypeTree(selector.tpe)), cases)
- case _ =>
- tree
- }
- tree1 match {
- case EmptyTree | TypeTree() =>
- tree1 setType erasure(tree1.tpe)
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
- val result = super.transform(tree1) setType null
- tpt.tpe = erasure(tree.symbol.tpe).resultType
- result
- case _ =>
- case class LoopControl(count: Int, ex : AssertionError) extends Throwable(ex.getMessage) with ControlThrowable
+ case Literal(ct) if ct.tag == ClassTag
+ && ct.typeValue.typeSymbol != definitions.UnitClass =>
+ treeCopy.Literal(tree, Constant(erasure(ct.typeValue)))
- try super.transform(tree1) setType null
- catch {
- case LoopControl(n, ex) if n <= 5 =>
- Console.println(tree1)
- throw LoopControl(n + 1, ex)
- }
- }
+ case _ =>
+ tree
}
+
+ override def transform(tree: Tree): Tree =
+ if (tree.symbol == ArrayClass && !tree.isType) tree // !!! needed?
+ else {
+ val tree1 = preErase(tree)
+ // println("preErase: "+ tree +" = "+ tree1)
+ val res = tree1 match {
+ case EmptyTree | TypeTree() =>
+ tree1 setType erasure(tree1.tpe)
+ case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ val result = super.transform(tree1) setType null
+ tpt.tpe = erasure(tree1.symbol.tpe).resultType
+ result
+ case _ =>
+ super.transform(tree1) setType null
+ }
+ // println("xform: "+ res)
+ res
+ }
}
/** The main transform function: Pretransfom the tree, and then
@@ -1090,4 +1095,4 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
}
}
}
-}
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index c228ee0e46..d1b3142c8a 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -231,12 +231,27 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
for (member <- impl.info.decls.toList) {
if (isForwarded(member)) {
val imember = member.overriddenSymbol(iface)
- //Console.println("mixin member "+member+":"+member.tpe+member.locationString+" "+imember+" "+imember.overridingSymbol(clazz)+" to "+clazz+" with scope "+clazz.info.decls)//DEBUG
+ // atPhase(currentRun.erasurePhase){
+ // println(""+(clazz, iface, clazz.typeParams, iface.typeParams, imember, clazz.thisType.baseType(iface), clazz.thisType.baseType(iface).memberInfo(imember), imember.info substSym(iface.typeParams, clazz.typeParams) ))
+ // }
+ // Console.println("mixin member "+member+":"+member.tpe+member.locationString+" "+imember+" "+imember.overridingSymbol(clazz)+" to "+clazz+" with scope "+clazz.info.decls)//DEBUG
if (imember.overridingSymbol(clazz) == NoSymbol &&
clazz.info.findMember(member.name, 0, lateDEFERRED, false).alternatives.contains(imember)) {
+ val newSym = atPhase(currentRun.erasurePhase){
+ val res = imember.cloneSymbol(clazz)
+ // since we used the member (imember) from the interface that represents the trait that's being mixed in,
+ // have to instantiate the interface type params (that may occur in imember's info) as they are seen from the class
+ // we can't use the member that we get from the implementation class, as it's a clone that was made after erasure,
+ // and thus it does not know its info at the beginning of erasure anymore
+ // optimize: no need if iface has no typeparams
+ if(iface.typeParams nonEmpty) res.setInfo(clazz.thisType.baseType(iface).memberInfo(imember))
+ res
+ } // clone before erasure got rid of type info we'll need to generate a javaSig
+ // now we'll have the type info at (the beginning of) erasure in our history,
+ newSym.updateInfo(imember.info.cloneInfo(newSym)) // and now newSym has the info that's been transformed to fit this period (no need for asSeenFrom as phase.erasedTypes)
val member1 = addMember(
clazz,
- member.cloneSymbol(clazz) setPos clazz.pos resetFlag (DEFERRED | lateDEFERRED))
+ newSym setPos clazz.pos resetFlag (DEFERRED | lateDEFERRED))
member1.asInstanceOf[TermSymbol] setAlias member;
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
index 6656b79d26..de5355de61 100644
--- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
+++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
@@ -26,7 +26,7 @@ trait TypingTransformers {
protected def typedPos(pos: Position)(tree: Tree) = localTyper typed { atPos(pos)(tree) }
/** a typer for each enclosing class */
- var typers: Map[Symbol, analyzer.Typer] = new HashMap
+ val typers: Map[Symbol, analyzer.Typer] = new HashMap
override def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans)
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 0270323133..8e3722dd99 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -62,6 +62,8 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
case MethodType(params, ExistentialType(tparams, restpe @ MethodType(_, _))) =>
assert(false, "unexpected curried method types with intervening existential")
tp0
+ case MethodType(h :: t, restpe) if h.isImplicit =>
+ apply(MethodType(h.cloneSymbol.resetFlag(IMPLICIT) :: t, restpe))
case PolyType(List(), restpe) => // nullary method type
apply(MethodType(List(), restpe))
case PolyType(tparams, restpe) => // polymorphic nullary method type, since it didn't occur in a higher-kinded position
@@ -398,7 +400,7 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
val predef = gen.mkAttributedRef(PredefModule)
val meth =
if ((elemtp <:< AnyRefClass.tpe) && !isPhantomClass(elemtp.typeSymbol))
- Select(predef, "wrapRefArray")
+ TypeApply(Select(predef, "wrapRefArray"), List(TypeTree(elemtp)))
else if (isValueClass(elemtp.typeSymbol))
Select(predef, "wrap"+elemtp.typeSymbol.name+"Array")
else
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 7469388a08..42c1329edf 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -8,6 +8,7 @@ package typechecker
import symtab.Flags._
import scala.collection.mutable.ListBuffer
+import annotation.tailrec
/** This trait ...
*
@@ -349,9 +350,26 @@ trait Contexts { self: Analyzer =>
* @return ...
*/
def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean): Boolean = {
+ @inline def accessWithinLinked(ab: Symbol) = {
+ val linked = ab.linkedClassOfClass
+ // don't have access if there is no linked class
+ // (before adding the `ne NoSymbol` check, this was a no-op when linked eq NoSymbol,
+ // since `accessWithin(NoSymbol) == true` whatever the symbol)
+ (linked ne NoSymbol) && accessWithin(linked)
+ }
+
+ /** Are we inside definition of `ab'? */
+ def accessWithin(ab: Symbol) = {
+ // #3663: we must disregard package nesting if sym isJavaDefined
+ if(sym.isJavaDefined) {
+ // is `o` or one of its transitive owners equal to `ab`?
+ // stops at first package, since further owners can only be surrounding packages
+ @tailrec def abEnclosesStopAtPkg(o: Symbol): Boolean =
+ (o eq ab) || (!o.isPackageClass && (o ne NoSymbol) && abEnclosesStopAtPkg(o.owner))
+ abEnclosesStopAtPkg(owner)
+ } else (owner hasTransOwner ab)
+ }
- /** Are we inside definition of `sym'? */
- def accessWithin(sym: Symbol): Boolean = this.owner.ownersIterator contains sym
/*
var c = this
while (c != NoContext && c.owner != owner) {
@@ -373,18 +391,20 @@ trait Contexts { self: Analyzer =>
(pre == NoPrefix) || {
val ab = sym.accessBoundary(sym.owner)
- ((ab.isTerm || ab == definitions.RootClass)
- ||
- (accessWithin(ab) || accessWithin(ab.linkedClassOfClass)) &&
- (!sym.hasFlag(LOCAL) ||
- sym.owner.isImplClass || // allow private local accesses to impl classes
- (sym hasFlag PROTECTED) && isSubThisType(pre, sym.owner) ||
- pre =:= sym.owner.thisType)
- ||
- (sym hasFlag PROTECTED) &&
- (superAccess || sym.isConstructor ||
- (pre.widen.typeSymbol.isNonBottomSubClass(sym.owner) &&
- (isSubClassOfEnclosing(pre.widen.typeSymbol) || phase.erasedTypes))))
+ ( (ab.isTerm || ab == definitions.RootClass)
+ || (accessWithin(ab) || accessWithinLinked(ab)) &&
+ ( !sym.hasFlag(LOCAL)
+ || sym.owner.isImplClass // allow private local accesses to impl classes
+ || (sym hasFlag PROTECTED) && isSubThisType(pre, sym.owner)
+ || pre =:= sym.owner.thisType
+ )
+ || (sym hasFlag PROTECTED) &&
+ ( superAccess
+ || sym.isConstructor
+ || (pre.widen.typeSymbol.isNonBottomSubClass(sym.owner) &&
+ (isSubClassOfEnclosing(pre.widen.typeSymbol) || phase.erasedTypes))
+ )
+ )
// note: phase.erasedTypes disables last test, because after addinterfaces
// implementation classes are not in the superclass chain. If we enable the
// test, bug780 fails.
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 3abaf4f337..0056dcd917 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -27,6 +27,7 @@ self: Analyzer =>
import definitions._
def traceImplicits = printTypings
+ import global.typer.{printTyping, deindentTyping, indentTyping}
/** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch`
* for more info how the search is conducted.
@@ -42,6 +43,8 @@ self: Analyzer =>
* @return A search result
*/
def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = {
+ printTyping("Beginning implicit search for "+ tree +" expecting "+ pt + (if(isView) " looking for a view" else ""))
+ indentTyping()
val rawTypeStart = startCounter(rawTypeImpl)
val findMemberStart = startCounter(findMemberImpl)
val subtypeStart = startCounter(subtypeImpl)
@@ -54,6 +57,8 @@ self: Analyzer =>
stopCounter(rawTypeImpl, rawTypeStart)
stopCounter(findMemberImpl, findMemberStart)
stopCounter(subtypeImpl, subtypeStart)
+ deindentTyping()
+ printTyping("Implicit search yielded: "+ result)
result
}
@@ -209,11 +214,10 @@ self: Analyzer =>
*/
class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context)
extends Typer(context0) {
-
+ printTyping("begin implicit search: "+(tree, pt, isView, context.outer.undetparams))
// assert(tree.isEmpty || tree.pos.isDefined, tree)
import infer._
-
/** Is implicit info `info1` better than implicit info `info2`?
*/
def improves(info1: ImplicitInfo, info2: ImplicitInfo) = {
@@ -424,7 +428,7 @@ self: Analyzer =>
incCounter(plausiblyCompatibleImplicits)
- //if (traceImplicits) println("typed impl for "+wildPt+"? "+info.name+":"+depoly(info.tpe)+"/"+undetParams+"/"+isPlausiblyCompatible(info.tpe, wildPt)+"/"+matchesPt(depoly(info.tpe), wildPt, List())+"/"+info.pre+"/"+isStable(info.pre))
+ printTyping("typed impl for "+wildPt+"? "+info.name +":"+ depoly(info.tpe)+ " orig info= "+ info.tpe +"/"+undetParams+"/"+isPlausiblyCompatible(info.tpe, wildPt)+"/"+matchesPt(depoly(info.tpe), wildPt, List())+"/"+info.pre+"/"+isStable(info.pre))
if (matchesPt(depoly(info.tpe), wildPt, List()) && isStable(info.pre)) {
incCounter(matchingImplicits)
@@ -433,7 +437,7 @@ self: Analyzer =>
if (info.pre == NoPrefix) Ident(info.name)
else Select(gen.mkAttributedQualifier(info.pre), info.name)
}
- if (traceImplicits) println("typed impl?? "+info.name+":"+info.tpe+" ==> "+itree+" with pt = "+pt+", wildpt = "+wildPt)
+ printTyping("typedImplicit0 typing"+ itree +" with wildpt = "+ wildPt +" from implicit "+ info.name+":"+info.tpe)
def fail(reason: String): SearchResult = {
if (settings.XlogImplicits.value)
inform(itree+" is not a valid implicit value for "+pt+" because:\n"+reason)
@@ -452,10 +456,10 @@ self: Analyzer =>
incCounter(typedImplicits)
- if (traceImplicits) println("typed implicit "+itree1+":"+itree1.tpe+", pt = "+wildPt)
+ printTyping("typed implicit "+itree1+":"+itree1.tpe+", pt = "+wildPt)
val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun }
else adapt(itree1, EXPRmode, wildPt)
- if (traceImplicits) println("adapted implicit "+itree1.symbol+":"+itree2.tpe+" to "+wildPt)
+ printTyping("adapted implicit "+itree1.symbol+":"+itree2.tpe+" to "+wildPt)
def hasMatchingSymbol(tree: Tree): Boolean = (tree.symbol == info.sym) || {
tree match {
case Apply(fun, _) => hasMatchingSymbol(fun)
@@ -469,7 +473,7 @@ self: Analyzer =>
else if (hasMatchingSymbol(itree1)) {
val tvars = undetParams map freshVar
if (matchesPt(itree2.tpe, pt.instantiateTypeParams(undetParams, tvars), undetParams)) {
- if (traceImplicits) println("tvars = "+tvars+"/"+(tvars map (_.constr)))
+ printTyping("tvars = "+tvars+"/"+(tvars map (_.constr)))
val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt),
false, lubDepth(List(itree2.tpe, pt)))
@@ -478,7 +482,7 @@ self: Analyzer =>
// filter out failures from type inference, don't want to remove them from undetParams!
// we must be conservative in leaving type params in undetparams
- val (okParams, okArgs, _) = adjustTypeArgs(undetParams, targs) // prototype == WildcardType: want to remove all inferred Nothing's
+ val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, targs) // prototype == WildcardType: want to remove all inferred Nothing's
val subst = new TreeTypeSubstituter(okParams, okArgs)
subst traverse itree2
@@ -498,7 +502,8 @@ self: Analyzer =>
// println("RESULT = "+itree+"///"+itree1+"///"+itree2)//DEBUG
result
} else {
- if (traceImplicits) println("incompatible: "+itree2.tpe+" does not match "+pt.instantiateTypeParams(undetParams, tvars))
+ printTyping("incompatible: "+itree2.tpe+" does not match "+pt.instantiateTypeParams(undetParams, tvars))
+
SearchFailure
}
} else if (settings.XlogImplicits.value)
@@ -810,7 +815,7 @@ self: Analyzer =>
def mot(tp0: Type): Tree = {
val tp1 = tp0.normalize
tp1 match {
- case ThisType(_) | SingleType(_, _) =>
+ case ThisType(_) | SingleType(_, _) if !(tp1 exists {tp => tp.typeSymbol.isExistentiallyBound}) => // can't generate a reference to a value that's abstracted over by an existential
manifestFactoryCall("singleType", tp, gen.mkAttributedQualifier(tp1))
case ConstantType(value) =>
manifestOfType(tp1.deconst, full)
@@ -840,6 +845,8 @@ self: Analyzer =>
} else if (sym.isExistentiallyBound && full) {
manifestFactoryCall("wildcardType", tp,
findManifest(tp.bounds.lo), findManifest(tp.bounds.hi))
+ } else if(undetParams contains sym) { // looking for a manifest of a type parameter that hasn't been inferred by now, can't do much, but let's not fail
+ mot(NothingClass.tpe) // TODO: should we include the mapping from sym -> NothingClass.tpe in the SearchResult? (it'll get instantiated to nothing anyway, I think)
} else {
EmptyTree // a manifest should have been found by normal searchImplicit
}
@@ -924,5 +931,46 @@ self: Analyzer =>
}
}
+ object ImplicitNotFoundMsg {
+ def unapply(sym: Symbol): Option[(Message)] = sym.implicitNotFoundMsg map (m => (new Message(sym, m)))
+ // check the message's syntax: should be a string literal that may contain occurences of the string "${X}",
+ // where `X` refers to a type parameter of `sym`
+ def check(sym: Symbol): Option[String] =
+ sym.getAnnotation(ImplicitNotFoundClass).flatMap(_.stringArg(0) match {
+ case Some(m) => new Message(sym, m) validate
+ case None => Some("Missing argument `msg` on implicitNotFound annotation.")
+ })
+
+
+ class Message(sym: Symbol, msg: String) {
+ // http://dcsobral.blogspot.com/2010/01/string-interpolation-in-scala-with.html
+ private def interpolate(text: String, vars: Map[String, String]) = { import scala.util.matching.Regex
+ """\$\{([^}]+)\}""".r.replaceAllIn(text, (_: Regex.Match) match {
+ case Regex.Groups(v) => vars.getOrElse(v, "")
+ })}
+
+ private lazy val typeParamNames: List[String] = sym.typeParams.map(_.decodedName)
+
+ def format(paramName: Name, paramTp: Type): String = format(paramTp.typeArgs map (_.toString))
+ def format(typeArgs: List[String]): String =
+ interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc?
+
+ def validate: Option[String] = {
+ import scala.util.matching.Regex; import collection.breakOut
+ // is there a shorter way to avoid the intermediate toList?
+ val refs = Set("""\$\{([^}]+)\}""".r.findAllIn(msg).matchData.map(_.group(1)).toList : _*)
+ val decls = Set(typeParamNames : _*)
+ (refs &~ decls) match {
+ case s if s isEmpty => None
+ case unboundNames =>
+ val singular = unboundNames.size == 1
+ Some("The type parameter"+( if(singular) " " else "s " )+ unboundNames.mkString(", ") +
+ " referenced in the message of the @implicitNotFound annotation "+( if(singular) "is" else "are" )+
+ " not defined by "+ sym +".")
+ }
+ }
+ }
+ }
+
private val DivergentImplicit = new Exception()
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 89346cbdab..a02f384a8d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -242,6 +242,9 @@ trait Infer {
/** Check that <code>sym</code> is defined and accessible as a member of
* tree <code>site</code> with type <code>pre</code> in current context.
+ *
+ * Note: pre is not refchecked -- moreover, refchecking the resulting tree may not refcheck pre,
+ * since pre may not occur in its type (callers should wrap the result in a TypeTreeWithDeferredRefCheck)
*/
def checkAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): Tree =
if (sym.isError) {
@@ -515,48 +518,64 @@ trait Infer {
tvars map (tvar => WildcardType)
}
+ object AdjustedTypeArgs {
+ type Result = collection.mutable.LinkedHashMap[Symbol, Option[Type]]
+
+ def unapply(m: Result): Some[(List[Symbol], List[Type])] = Some(toLists(
+ m collect {case (p, Some(a)) => (p, a)} unzip ))
+
+ object Undets {
+ def unapply(m: Result): Some[(List[Symbol], List[Type], List[Symbol])] = Some(toLists{
+ val (ok, nok) = m.map{case (p, a) => (p, a.getOrElse(null))}.partition(_._2 ne null)
+ val (okArgs, okTparams) = ok.unzip
+ (okArgs, okTparams, nok.keys)
+ })
+ }
+
+ object AllArgsAndUndets {
+ def unapply(m: Result): Some[(List[Symbol], List[Type], List[Type], List[Symbol])] = Some(toLists{
+ val (ok, nok) = m.map{case (p, a) => (p, a.getOrElse(null))}.partition(_._2 ne null)
+ val (okArgs, okTparams) = ok.unzip
+ (okArgs, okTparams, m.values.map(_.getOrElse(NothingClass.tpe)), nok.keys)
+ })
+ }
+
+ @inline private def toLists[A1, A2](pxs: (Iterable[A1], Iterable[A2])) = (pxs._1.toList, pxs._2.toList)
+ @inline private def toLists[A1, A2, A3](pxs: (Iterable[A1], Iterable[A2], Iterable[A3])) = (pxs._1.toList, pxs._2.toList, pxs._3.toList)
+ @inline private def toLists[A1, A2, A3, A4](pxs: (Iterable[A1], Iterable[A2], Iterable[A3], Iterable[A4])) = (pxs._1.toList, pxs._2.toList, pxs._3.toList, pxs._4.toList)
+ }
+
/** Retract arguments that were inferred to Nothing because inference failed. Correct types for repeated params.
*
* We detect Nothing-due-to-failure by only retracting a parameter if either:
* - it occurs in an invariant/contravariant position in `restpe`
* - `restpe == WildcardType`
*
- * Retracted parameters are collected in `uninstantiated`.
+ * Retracted parameters are mapped to None.
+ * TODO:
+ * - make sure the performance hit of storing these in a map is acceptable (it's going to be a small map in 90% of the cases, I think)
+ * - refactor further up the callstack so that we don't have to do this post-factum adjustment?
*
* Rewrite for repeated param types: Map T* entries to Seq[T].
- * @return (okTparams, okArgs, leftUndet)
- * * okTparams, okArgs: lists of tparam symbols and their inferred types
- * * leftUndet a list of remaining uninstantiated type parameters after inference
- * (type parameters mapped by the constraint solver to `scala.Nothing'
- * and not covariant in <code>restpe</code> are taken to be
- * uninstantiated. Maps all those type arguments to their
- * corresponding type parameters).
+ * @return map from tparams to inferred arg, if inference was successful, tparams that map to None are considered left undetermined
+ * type parameters that are inferred as `scala.Nothing' and that are not covariant in <code>restpe</code> are taken to be undetermined
*/
- def adjustTypeArgs(tparams: List[Symbol], targs: List[Type], restpe: Type = WildcardType): (List[Symbol], List[Type], List[Symbol]) = {
+ def adjustTypeArgs(tparams: List[Symbol], targs: List[Type], restpe: Type = WildcardType): AdjustedTypeArgs.Result = {
@inline def notCovariantIn(tparam: Symbol, restpe: Type) =
(varianceInType(restpe)(tparam) & COVARIANT) == 0 // tparam occurred non-covariantly (in invariant or contravariant position)
- val leftUndet = new ListBuffer[Symbol]
- val okParams = new ListBuffer[Symbol]
- val okArgs = new ListBuffer[Type]
-
- (tparams, targs).zipped foreach { (tparam, targ) =>
+ (tparams, targs).zipped.map{ (tparam, targ) =>
if (targ.typeSymbol == NothingClass &&
(isWildcard(restpe) || notCovariantIn(tparam, restpe))) {
- leftUndet += tparam
- // don't add anything to okArgs, it'll be filtered out later anyway
- // used `tparam.tpeHK` as dummy before
+ tparam -> None
} else {
- okParams += tparam
- okArgs += (
+ tparam -> Some(
if (targ.typeSymbol == RepeatedParamClass) targ.baseType(SeqClass)
else if (targ.typeSymbol == JavaRepeatedParamClass) targ.baseType(ArrayClass)
else targ.widen
)
}
- }
-
- (okParams.toList, okArgs.toList, leftUndet.toList)
+ }(collection.breakOut)
}
/** Return inferred type arguments, given type parameters, formal parameters,
@@ -572,18 +591,12 @@ trait Infer {
* @param restp the result type of the method
* @param argtpes the argument types of the application
* @param pt the expected return type of the application
- * @return (okTparams, okArgs, leftUndet)
- * * okTparams, okArgs: lists of tparam symbols and their inferred types
- * * leftUndet a list of remaining uninstantiated type parameters after inference
- * (type parameters mapped by the constraint solver to `scala.Nothing'
- * and not covariant in <code>restpe</code> are taken to be
- * uninstantiated. Maps all those type arguments to their
- * corresponding type parameters).
+ * @return @see adjustTypeArgs
* @throws NoInstance
*/
def methTypeArgs(tparams: List[Symbol], formals: List[Type], restpe: Type,
- argtpes: List[Type], pt: Type): (List[Symbol], List[Type], List[Symbol]) = {
+ argtpes: List[Type], pt: Type): AdjustedTypeArgs.Result = {
val tvars = tparams map freshVar
if (inferInfo)
println("methTypeArgs tparams = "+tparams+
@@ -757,7 +770,7 @@ trait Infer {
isCompatibleArgs(argtpes, formals) && isWeaklyCompatible(restpe, pt)
} else {
try {
- val (okparams, okargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt)
+ val AdjustedTypeArgs.Undets(okparams, okargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt)
// #2665: must use weak conformance, not regular one (follow the monomorphic case above)
(exprTypeArgs(leftUndet, restpe.instantiateTypeParams(okparams, okargs), pt, isWeaklyCompatible) ne null) &&
isWithinBounds(NoPrefix, NoSymbol, okparams, okargs)
@@ -1010,71 +1023,8 @@ trait Infer {
}
}
- /** Check whether <arg>sym1</arg>'s variance conforms to <arg>sym2</arg>'s variance
- *
- * If <arg>sym2</arg> is invariant, <arg>sym1</arg>'s variance is irrelevant. Otherwise they must be equal.
- */
- def variancesMatch(sym1: Symbol, sym2: Symbol): Boolean = (sym2.variance==0 || sym1.variance==sym2.variance)
- /** Check well-kindedness of type application (assumes arities are already checked) -- @M
- *
- * This check is also performed when abstract type members become concrete (aka a "type alias") -- then tparams.length==1
- * (checked one type member at a time -- in that case, prefix is the name of the type alias)
- *
- * Type application is just like value application: it's "contravariant" in the sense that
- * the type parameters of the supplied type arguments must conform to the type parameters of
- * the required type parameters:
- * - their bounds must be less strict
- * - variances must match (here, variances are absolute, the variance of a type parameter does not influence the variance of its higher-order parameters)
- * - @M TODO: are these conditions correct,sufficient&necessary?
- *
- * e.g. class Iterable[t, m[+x <: t]] --> the application Iterable[Int, List] is okay, since
- * List's type parameter is also covariant and its bounds are weaker than <: Int
- */
def checkKindBounds(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol): List[String] = {
- def transform(tp: Type, clazz: Symbol): Type = tp.asSeenFrom(pre, clazz) // instantiate type params that come from outside the abstract type we're currently checking
-
- // check that the type parameters <arg>hkargs</arg> to a higher-kinded type conform to the expected params <arg>hkparams</arg>
- def checkKindBoundsHK(hkargs: List[Symbol], arg: Symbol, param: Symbol, paramowner: Symbol): (List[(Symbol, Symbol)], List[(Symbol, Symbol)], List[(Symbol, Symbol)]) = {
- // @M sometimes hkargs != arg.typeParams, the symbol and the type may have very different type parameters
- val hkparams = param.typeParams
-
- if(hkargs.length != hkparams.length) {
- if(arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded
- else (List((arg, param)), Nil, Nil)
- } else {
- val _arityMismatches = new ListBuffer[(Symbol, Symbol)]
- val _varianceMismatches = new ListBuffer[(Symbol, Symbol)]
- val _stricterBounds = new ListBuffer[(Symbol, Symbol)]
- def varianceMismatch(a: Symbol, p: Symbol) { _varianceMismatches += ((a, p)) }
- def stricterBound(a: Symbol, p: Symbol) { _stricterBounds += ((a, p)) }
- def arityMismatches(as: Iterable[(Symbol, Symbol)]) { _arityMismatches ++= as }
- def varianceMismatches(as: Iterable[(Symbol, Symbol)]) { _varianceMismatches ++= as }
- def stricterBounds(as: Iterable[(Symbol, Symbol)]) { _stricterBounds ++= as }
-
- for ((hkarg, hkparam) <- hkargs zip hkparams) {
- if (hkparam.typeParams.isEmpty) { // base-case: kind *
- if (!variancesMatch(hkarg, hkparam))
- varianceMismatch(hkarg, hkparam)
-
- // instantiateTypeParams(tparams, targs) --> higher-order bounds may contain references to type arguments
- // substSym(hkparams, hkargs) --> these types are going to be compared as types of kind *
- // --> their arguments use different symbols, but are conceptually the same
- // (could also replace the types by polytypes, but can't just strip the symbols, as ordering is lost then)
- if (!(transform(hkparam.info.instantiateTypeParams(tparams, targs).bounds.substSym(hkparams, hkargs), paramowner) <:< transform(hkarg.info.bounds, owner)))
- stricterBound(hkarg, hkparam)
- } else {
- val (am, vm, sb) = checkKindBoundsHK(hkarg.typeParams, hkarg, hkparam, paramowner)
- arityMismatches(am)
- varianceMismatches(vm)
- stricterBounds(sb)
- }
- }
-
- (_arityMismatches.toList, _varianceMismatches.toList, _stricterBounds.toList)
- }
- }
-
// @M TODO this method is duplicated all over the place (varianceString)
def varStr(s: Symbol): String =
if (s.isCovariant) "covariant"
@@ -1090,32 +1040,22 @@ trait Infer {
}
}
- val errors = new ListBuffer[String]
- (tparams zip targs).foreach{ case (tparam, targ) if (targ.isHigherKinded || !tparam.typeParams.isEmpty) =>
- // @M must use the typeParams of the type targ, not the typeParams of the symbol of targ!!
- val tparamsHO = targ.typeParams
-
- val (arityMismatches, varianceMismatches, stricterBounds) =
- checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner) // NOTE: *not* targ.typeSymbol, which normalizes
- if (!(arityMismatches.isEmpty && varianceMismatches.isEmpty && stricterBounds.isEmpty)){
- errors += (targ+"'s type parameters do not match "+tparam+"'s expected parameters: "+
- (for ((a, p) <- arityMismatches)
- yield a+qualify(a,p)+ " has "+reporter.countElementsAsString(a.typeParams.length, "type parameter")+", but "+
- p+qualify(p,a)+" has "+reporter.countAsString(p.typeParams.length)).toList.mkString(", ") +
- (for ((a, p) <- varianceMismatches)
- yield a+qualify(a,p)+ " is "+varStr(a)+", but "+
- p+qualify(p,a)+" is declared "+varStr(p)).toList.mkString(", ") +
- (for ((a, p) <- stricterBounds)
- yield a+qualify(a,p)+"'s bounds "+a.info+" are stricter than "+
- p+qualify(p,a)+"'s declared bounds "+p.info).toList.mkString(", "))
- }
- // case (tparam, targ) => println("no check: "+(tparam, targ, tparam.typeParams.isEmpty))
- case _ =>
+ val errors = checkKindBounds0(tparams, targs, pre, owner, true)
+ val errorMessages = new ListBuffer[String]
+ errors foreach {case (targ, tparam, arityMismatches, varianceMismatches, stricterBounds) => errorMessages +=
+ (targ+"'s type parameters do not match "+tparam+"'s expected parameters: "+
+ (for ((a, p) <- arityMismatches)
+ yield a+qualify(a,p)+ " has "+reporter.countElementsAsString(a.typeParams.length, "type parameter")+", but "+
+ p+qualify(p,a)+" has "+reporter.countAsString(p.typeParams.length)).toList.mkString(", ") +
+ (for ((a, p) <- varianceMismatches)
+ yield a+qualify(a,p)+ " is "+varStr(a)+", but "+
+ p+qualify(p,a)+" is declared "+varStr(p)).toList.mkString(", ") +
+ (for ((a, p) <- stricterBounds)
+ yield a+qualify(a,p)+"'s bounds "+a.info+" are stricter than "+
+ p+qualify(p,a)+"'s declared bounds "+p.info).toList.mkString(", "))
}
-
- errors.toList
+ errorMessages.toList
}
-
/** Substitite free type variables `undetparams' of polymorphic argument
* expression `tree', given two prototypes `strictPt', and `lenientPt'.
* `strictPt' is the first attempt prototype where type parameters
@@ -1151,14 +1091,16 @@ trait Infer {
" tparams = "+tparams+"\n"+
" pt = "+pt)
val targs = exprTypeArgs(tparams, tree.tpe, pt)
- val (okParams, okArgs, leftUndet) = // TODO AM: is this pattern match too expensive? should we push it down into the else of the if below?
- if (keepNothings || (targs eq null)) (tparams, targs, List()) //@M: adjustTypeArgs fails if targs==null, neg/t0226
- else adjustTypeArgs(tparams, targs)
-
- if (inferInfo) println("inferred expr instance for "+ tree +" --> (okParams, okArgs, leftUndet)= "+(okParams, okArgs, leftUndet))
- substExpr(tree, okParams, okArgs, pt)
- leftUndet
+ if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226
+ substExpr(tree, tparams, targs, pt)
+ List()
+ } else {
+ val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, targs)
+ if (inferInfo) println("inferred expr instance for "+ tree +" --> (okParams, okArgs, leftUndet)= "+(okParams, okArgs, leftUndet))
+ substExpr(tree, okParams, okArgs, pt)
+ leftUndet
+ }
}
/** Substitite free type variables `undetparams' of polymorphic argument
@@ -1180,14 +1122,15 @@ trait Infer {
}
}
- /** Substitite free type variables <code>undetparams</code> of application
+ /** Substitute free type variables <code>undetparams</code> of application
* <code>fn(args)</code>, given prototype <code>pt</code>.
*
* @param fn ...
* @param undetparams ...
* @param args ...
* @param pt ...
- * @return Return the list of type parameters that remain uninstantiated.
+ * @return The type parameters that remain uninstantiated,
+ * and that thus have not been substituted.
*/
def inferMethodInstance(fn: Tree, undetparams: List[Symbol],
args: List[Tree], pt0: Type): List[Symbol] = fn.tpe match {
@@ -1202,8 +1145,8 @@ trait Infer {
val formals = formalTypes(params0 map (_.tpe), args.length)
val argtpes = actualTypes(args map (_.tpe.deconst), formals.length)
val restpe = fn.tpe.resultType(argtpes)
- val (okparams, okargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt)
- checkBounds(fn.pos, NoPrefix, NoSymbol, okparams, okargs, "inferred ")
+ val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt)
+ checkBounds(fn.pos, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")
val treeSubst = new TreeTypeSubstituter(okparams, okargs)
treeSubst.traverse(fn)
treeSubst.traverseTrees(args)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index c0948a0752..7f68876327 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -299,7 +299,7 @@ trait Namers { self: Analyzer =>
val sym = tree.symbol
if (settings.debug.value) log("entered " + sym + " in " + context.owner + ", scope-id = " + context.scope.## )
var ltype = namerOf(sym).typeCompleter(tree)
- if (!tparams.isEmpty) {
+ if (tparams nonEmpty) {
//@M! TypeDef's type params are handled differently
//@M e.g., in [A[x <: B], B], A and B are entered first as both are in scope in the definition of x
//@M x is only in scope in `A[x <: B]'
@@ -376,8 +376,9 @@ trait Namers { self: Analyzer =>
name.endsWith(nme.OUTER, nme.OUTER.length) ||
context.unit.isJava) &&
!mods.isLazy) {
- tree.symbol = enterInScope(owner.newValue(tree.pos, name)
- .setFlag(mods.flags))
+ val vsym = owner.newValue(tree.pos, name).setFlag(mods.flags);
+ if(context.unit.isJava) setPrivateWithin(tree, vsym, mods) // #3663
+ tree.symbol = enterInScope(vsym)
finish
} else {
val mods1 =
@@ -420,7 +421,7 @@ trait Namers { self: Analyzer =>
addBeanGetterSetter(vd, getter)
}
case DefDef(mods, nme.CONSTRUCTOR, tparams, _, _, _) =>
- var sym = owner.newConstructor(tree.pos).setFlag(mods.flags | owner.getFlag(ConstrFlags))
+ val sym = owner.newConstructor(tree.pos).setFlag(mods.flags | owner.getFlag(ConstrFlags))
setPrivateWithin(tree, sym, mods)
tree.symbol = enterInScope(sym)
finishWith(tparams)
@@ -430,7 +431,7 @@ trait Namers { self: Analyzer =>
case TypeDef(mods, name, tparams, _) =>
var flags: Long = mods.flags
if ((flags & PARAM) != 0L) flags |= DEFERRED
- var sym = new TypeSymbol(owner, tree.pos, name).setFlag(flags)
+ val sym = new TypeSymbol(owner, tree.pos, name).setFlag(flags)
setPrivateWithin(tree, sym, mods)
tree.symbol = enterInScope(sym)
finishWith(tparams)
@@ -1070,23 +1071,17 @@ trait Namers { self: Analyzer =>
tp
}
- def verifyOverriding(other: Symbol): Boolean = {
- if(other.unsafeTypeParams.length != tparamSyms.length) {
- context.error(tpsym.pos,
- "The kind of "+tpsym.keyString+" "+tpsym.varianceString + tpsym.nameString+
- " does not conform to the expected kind of " + other.defString + other.locationString + ".")
- false
- } else true
- }
-
- // @M: make sure overriding in refinements respects rudimentary kinding
- // have to do this early, as otherwise we might get crashes: (see neg/bug1275.scala)
- // suppose some parameterized type member is overridden by a type member w/o params,
- // then appliedType will be called on a type that does not expect type args --> crash
- if (tpsym.owner.isRefinementClass && // only needed in refinements
- !tpsym.allOverriddenSymbols.forall{verifyOverriding(_)})
- ErrorType
- else polyType(tparamSyms, tp)
+ // see neg/bug1275, #3419
+ // used to do a rudimentary kind check here to ensure overriding in refinements
+ // doesn't change a type member's arity (number of type parameters),
+ // e.g. trait T { type X[A] }; type S = T{type X}; val x: S
+ // X in x.X[A] will get rebound to the X in the refinement, which does not take any type parameters
+ // this mismatch does not crash the compiler (anymore), but leads to weird type errors,
+ // as x.X[A] will become NoType internally
+ // it's not obvious the errror refers to the X in the refinement and not the original X
+ // however, separate compilation requires the symbol info to be loaded to do this check,
+ // but loading the info will probably lead to spurious cyclic errors --> omit the check
+ polyType(tparamSyms, tp)
}
/** Given a case class
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 18ccbecafc..74114b455e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -421,8 +421,9 @@ abstract class RefChecks extends InfoTransform {
def javaErasedOverridingSym(sym: Symbol): Symbol =
clazz.tpe.nonPrivateMemberAdmitting(sym.name, BRIDGE).filter(other =>
!other.isDeferred && other.isJavaDefined && {
- val tp1 = erasure.erasure(clazz.thisType.memberType(sym))
- val tp2 = erasure.erasure(clazz.thisType.memberType(other))
+ def uncurryAndErase(tp: Type) = erasure.erasure(uncurry.transformInfo(sym, tp)) // #3622: erasure operates on uncurried types -- note on passing sym in both cases: only sym.isType is relevant for uncurry.transformInfo
+ val tp1 = uncurryAndErase(clazz.thisType.memberType(sym))
+ val tp2 = uncurryAndErase(clazz.thisType.memberType(other))
atPhase(currentRun.erasurePhase.next)(tp1 matches tp2)
})
@@ -1022,18 +1023,32 @@ abstract class RefChecks extends InfoTransform {
private def checkAnnotations(tpes: List[Type], pos: Position) = tpes foreach (tp => checkTypeRef(tp, pos))
private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f
- private def applyRefchecksToAnnotations(tree: Tree) = {
+ private def applyRefchecksToAnnotations(tree: Tree): Unit = {
def applyChecks(annots: List[AnnotationInfo]) = {
checkAnnotations(annots map (_.atp), tree.pos)
transformTrees(annots flatMap (_.args))
}
tree match {
- case m: MemberDef => applyChecks(m.symbol.annotations)
- case TypeTree() => doTypeTraversal(tree) {
- case AnnotatedType(annots, _, _) => applyChecks(annots)
- case _ =>
- }
+ case m: MemberDef =>
+ val sym = m.symbol
+ applyChecks(sym.annotations)
+ // validate implicitNotFoundMessage
+ analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn =>
+ unit.warning(tree.pos, "Invalid implicitNotFound message for %s%s:\n%s".format(sym, sym.locationString, warn))
+ }
+ case tpt@TypeTree() =>
+ if(tpt.original != null) {
+ tpt.original foreach {
+ case dc@TypeTreeWithDeferredRefCheck() => applyRefchecksToAnnotations(dc.check()) // #2416
+ case _ =>
+ }
+ }
+
+ doTypeTraversal(tree) {
+ case AnnotatedType(annots, _, _) => applyChecks(annots)
+ case _ =>
+ }
case _ =>
}
}
@@ -1143,7 +1158,6 @@ abstract class RefChecks extends InfoTransform {
// type bounds (bug #935), issues deprecation warnings for symbols used
// inside annotations.
applyRefchecksToAnnotations(tree)
-
var result: Tree = tree match {
case DefDef(mods, name, tparams, vparams, tpt, EmptyTree) if tree.symbol.hasAnnotation(NativeAttr) =>
tree.symbol.resetFlag(DEFERRED)
@@ -1164,7 +1178,17 @@ abstract class RefChecks extends InfoTransform {
if (bridges.nonEmpty) treeCopy.Template(tree, parents, self, body ::: bridges)
else tree
- case TypeTree() =>
+ case dc@TypeTreeWithDeferredRefCheck() => assert(false, "adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc"); dc
+ case tpt@TypeTree() =>
+ if(tpt.original != null) {
+ tpt.original foreach {
+ case dc@TypeTreeWithDeferredRefCheck() =>
+ transform(dc.check()) // #2416 -- only call transform to do refchecks, but discard results
+ // tpt has the right type if the deferred checks are ok
+ case _ =>
+ }
+ }
+
val existentialParams = new ListBuffer[Symbol]
doTypeTraversal(tree) { // check all bounds, except those that are
// existential type parameters
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 12c4adb356..5fdf9267d2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -191,6 +191,15 @@ trait Typers { self: Analyzer =>
argResultsBuff += inferImplicit(fun, paramTp, true, false, context)
}
+ def errorMessage(paramName: Name, paramTp: Type) =
+ paramTp.typeSymbol match {
+ case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp)
+ case _ =>
+ "could not find implicit value for "+
+ (if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX) "evidence parameter of type "
+ else "parameter "+paramName+": ")+paramTp
+ }
+
val argResults = argResultsBuff.toList
val args = argResults.zip(params) flatMap {
case (arg, param) =>
@@ -199,10 +208,7 @@ trait Typers { self: Analyzer =>
else List(atPos(arg.tree.pos)(new AssignOrNamedArg(Ident(param.name), (arg.tree))))
} else {
if (!param.hasFlag(DEFAULTPARAM))
- context.error(
- fun.pos, "could not find implicit value for "+
- (if (param.name startsWith nme.EVIDENCE_PARAM_PREFIX) "evidence parameter of type "
- else "parameter "+param.name+": ")+param.tpe)
+ context.error(fun.pos, errorMessage(param.name, param.tpe))
positional = false
Nil
}
@@ -555,6 +561,8 @@ trait Typers { self: Analyzer =>
* If symbol refers to package object, insert `.package` as second to last selector.
* (exception for some symbols in scala package which are dealiased immediately)
* Call checkAccessible, which sets tree's attributes.
+ * Also note that checkAccessible looks up sym on pre without checking that pre is well-formed
+ * (illegal type applications in pre will be skipped -- that's why typedSelect wraps the resulting tree in a TreeWithDeferredChecks)
* @return modified tree and new prefix type
*/
private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): (Tree, Type) =
@@ -802,15 +810,10 @@ trait Typers { self: Analyzer =>
context.undetparams = context.undetparams ::: tparams1
adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original)
case mt: MethodType if mt.isImplicit && ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1)
- if (!context.undetparams.isEmpty/* && (mode & POLYmode) == 0 disabled to make implicits in new collection work; we should revisit this. */) { // (9)
- // println("adapt IMT: "+(context.undetparams, pt)) //@MDEBUG
- context.undetparams = inferExprInstance(
- tree, context.extractUndetparams(), pt, mt.params exists (p => isManifest(p.tpe)))
- // if we are looking for a manifest, instantiate type to Nothing anyway,
- // as we would get amnbiguity errors otherwise. Example
- // Looking for a manifest of Nil: This mas many potential types,
- // so we need to instantiate to minimal type List[Nothing].
- }
+ if (context.undetparams nonEmpty) // (9) -- should revisit dropped condition `(mode & POLYmode) == 0`
+ // dropped so that type args of implicit method are inferred even if polymorphic expressions are allowed
+ // needed for implicits in 2.8 collection library -- maybe once #3346 is fixed, we can reinstate the condition?
+ context.undetparams = inferExprInstance(tree, context.extractUndetparams(), pt, false) // false: retract Nothing's that indicate failure, ambiguities in manifests are dealt with in manifestOfType
val typer1 = constrTyperIf(treeInfo.isSelfOrSuperConstrCall(tree))
if (original != EmptyTree && pt != WildcardType)
typer1.silent(tpr => tpr.typed(tpr.applyImplicitArgs(tree), mode, pt)) match {
@@ -1776,8 +1779,18 @@ trait Typers { self: Analyzer =>
}
def typedTypeDef(tdef: TypeDef): TypeDef = {
- reenterTypeParams(tdef.tparams) // @M!
- val tparams1 = tdef.tparams mapConserve (typedTypeDef) // @M!
+ def typeDefTyper = {
+ if(tdef.tparams isEmpty) Typer.this
+ else newTyper(context.makeNewScope(tdef, tdef.symbol))
+ }
+ typeDefTyper.typedTypeDef0(tdef)
+ }
+
+ // call typedTypeDef instead
+ // a TypeDef with type parameters must always be type checked in a new scope
+ private def typedTypeDef0(tdef: TypeDef): TypeDef = {
+ reenterTypeParams(tdef.tparams)
+ val tparams1 = tdef.tparams mapConserve {typedTypeDef(_)}
val typedMods = removeAnnotations(tdef.mods)
// complete lazy annotations
val annots = tdef.symbol.annotations
@@ -2815,16 +2828,6 @@ trait Typers { self: Analyzer =>
res
}
- class SymInstance(val sym: Symbol, val tp: Type) {
- override def equals(other: Any): Boolean = other match {
- case that: SymInstance =>
- this.sym == that.sym && this.tp =:= that.tp
- case _ =>
- false
- }
- override def hashCode: Int = sym.hashCode * 41 + tp.hashCode
- }
-
/** convert skolems to existentials */
def packedType(tree: Tree, owner: Symbol): Type = {
def defines(tree: Tree, sym: Symbol) =
@@ -2860,7 +2863,7 @@ trait Typers { self: Analyzer =>
}
}
// add all local symbols of `tp' to `localSyms'
- // expanding higher-kinded types into individual copies for each instance.
+ // TODO: expand higher-kinded types into individual copies for each instance.
def addLocals(tp: Type) {
val remainingSyms = new ListBuffer[Symbol]
def addIfLocal(sym: Symbol, tp: Type) {
@@ -2969,6 +2972,11 @@ trait Typers { self: Analyzer =>
errorTree(tree, treeSymTypeMsg(fun)+" does not take type parameters.")
}
+ private[this] var typingIndent: String = ""
+ @inline final def deindentTyping() = if (printTypings) typingIndent = typingIndent.substring(0, typingIndent.length() - 2)
+ @inline final def indentTyping() = if (printTypings) typingIndent += " "
+ @inline final def printTyping(s: => String) = if (printTypings) println(typingIndent+s)
+
/**
* @param tree ...
* @param mode ...
@@ -3286,7 +3294,7 @@ trait Typers { self: Analyzer =>
def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ex.pos)
if (fun :: tree :: args exists errorInResult) {
- if (printTypings) println("second try for: "+fun+" and "+args)
+ printTyping("second try for: "+fun+" and "+args)
val Select(qual, name) = fun
val args1 = tryTypedArgs(args, argMode(fun, mode), ex)
val qual1 =
@@ -3296,9 +3304,8 @@ trait Typers { self: Analyzer =>
val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos
return typed1(tree1, mode | SNDTRYmode, pt)
}
- } else if (printTypings) {
- println("no second try for "+fun+" and "+args+" because error not in result:"+ex.pos+"!="+tree.pos)
- }
+ } else printTyping("no second try for "+fun+" and "+args+" because error not in result:"+ex.pos+"!="+tree.pos)
+
reportTypeError(tree.pos, ex)
setError(tree)
}
@@ -3537,9 +3544,9 @@ trait Typers { self: Analyzer =>
case Select(_, _) => treeCopy.Select(tree, qual, name)
case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name)
}
- //if (name.toString == "Elem") println("typedSelect "+qual+":"+qual.tpe+" "+sym+"/"+tree1+":"+tree1.tpe)
val (tree2, pre2) = makeAccessible(tree1, sym, qual.tpe, qual)
val result = stabilize(tree2, pre2, mode, pt)
+
def isPotentialNullDeference() = {
phase.id <= currentRun.typerPhase.id &&
!sym.isConstructor &&
@@ -3550,7 +3557,20 @@ trait Typers { self: Analyzer =>
if (settings.Xchecknull.value && isPotentialNullDeference && unit != null)
unit.warning(tree.pos, "potential null pointer dereference: "+tree)
- result
+ result match {
+ // could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual?
+ case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs nonEmpty => // TODO: somehow the new qual is not checked in refchecks
+ treeCopy.SelectFromTypeTree(
+ result,
+ (TypeTreeWithDeferredRefCheck(){ () => val tp = qual.tpe; val sym = tp.typeSymbolDirect
+ // will execute during refchecks -- TODO: make private checkTypeRef in refchecks public and call that one?
+ checkBounds(qual.pos, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "")
+ qual // you only get to see the wrapped tree after running this check :-p
+ }) setType qual.tpe,
+ name)
+ case _ =>
+ result
+ }
}
}
@@ -3683,6 +3703,7 @@ trait Typers { self: Analyzer =>
else atPos(tree.pos)(Select(qual, name))
// atPos necessary because qualifier might come from startContext
val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual)
+ // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right?
stabilize(tree2, pre2, mode, pt)
}
}
@@ -3708,7 +3729,7 @@ trait Typers { self: Analyzer =>
} else {
val tparams = tpt1.symbol.typeParams
if (tparams.length == args.length) {
- // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
+ // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
val args1 =
if(!tpt1.symbol.rawInfo.isComplete)
args mapConserve (typedHigherKindedType(_, mode))
@@ -3719,11 +3740,8 @@ trait Typers { self: Analyzer =>
//@M! the polytype denotes the expected kind
}
val argtypes = args1 map (_.tpe)
- val owntype = if (tpt1.symbol.isClass || tpt1.symbol.isNonClassType)
- // @M! added the latter condition
- appliedType(tpt1.tpe, argtypes)
- else tpt1.tpe.instantiateTypeParams(tparams, argtypes)
- (args, tparams).zipped map { (arg, tparam) => arg match {
+
+ (args, tparams).zipped foreach { (arg, tparam) => arg match {
// note: can't use args1 in selector, because Bind's got replaced
case Bind(_, _) =>
if (arg.symbol.isAbstractType)
@@ -3733,7 +3751,17 @@ trait Typers { self: Analyzer =>
glb(List(arg.symbol.info.bounds.hi, tparam.info.bounds.hi.subst(tparams, argtypes))))
case _ =>
}}
- TypeTree(owntype) setOriginal(tree) // setPos tree.pos
+
+ val result = TypeTree(appliedType(tpt1.tpe, argtypes)) setOriginal(tree) // setPos tree.pos (done by setOriginal)
+ if(tpt1.tpe.isInstanceOf[PolyType]) // did the type application (performed by appliedType) involve an unchecked beta-reduction?
+ (TypeTreeWithDeferredRefCheck(){ () =>
+ // wrap the tree and include the bounds check -- refchecks will perform this check (that the beta reduction was indeed allowed) and unwrap
+ // we can't simply use original in refchecks because it does not contains types
+ // (and the only typed trees we have have been mangled so they're not quite the original tree anymore)
+ checkBounds(result.pos, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "")
+ result // you only get to see the wrapped tree after running this check :-p
+ }).setType(result.tpe)
+ else result
} else if (tparams.length == 0) {
errorTree(tree, tpt1.tpe+" does not take type parameters")
} else {
@@ -3774,7 +3802,7 @@ trait Typers { self: Analyzer =>
newTyper(context.makeNewScope(tree, sym)).typedDefDef(ddef)
case tdef @ TypeDef(_, _, _, _) =>
- newTyper(context.makeNewScope(tree, sym)).typedTypeDef(tdef)
+ typedTypeDef(tdef)
case ldef @ LabelDef(_, _, _) =>
labelTyper(ldef).typedLabelDef(ldef)
@@ -4045,7 +4073,7 @@ trait Typers { self: Analyzer =>
case SelectFromTypeTree(qual, selector) =>
val qual1 = typedType(qual, mode)
if (qual1.tpe.isVolatile) error(tree.pos, "illegal type selection from volatile type "+qual.tpe)
- typedSelect(typedType(qual, mode), selector)
+ typedSelect(qual1, selector)
case CompoundTypeTree(templ) =>
typedCompoundTypeTree(templ)
@@ -4061,6 +4089,7 @@ trait Typers { self: Analyzer =>
case etpt @ ExistentialTypeTree(_, _) =>
newTyper(context.makeNewScope(tree, context.owner)).typedExistentialTypeTree(etpt, mode)
+ case dc@TypeTreeWithDeferredRefCheck() => dc // TODO: should we re-type the wrapped tree? then we need to change TypeTreeWithDeferredRefCheck's representation to include the wrapped tree explicitly (instead of in its closure)
case tpt @ TypeTree() =>
if (tpt.original != null)
tree setType typedType(tpt.original, mode).tpe
@@ -4083,8 +4112,7 @@ trait Typers { self: Analyzer =>
* @param pt ...
* @return ...
*/
- def typed(tree: Tree, mode: Int, pt: Type): Tree = {
-
+ def typed(tree: Tree, mode: Int, pt: Type): Tree = { indentTyping()
def dropExistential(tp: Type): Type = tp match {
case ExistentialType(tparams, tpe) =>
if (settings.debug.value) println("drop ex "+tree+" "+tp)
@@ -4110,15 +4138,15 @@ trait Typers { self: Analyzer =>
tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
}
- if (printTypings) println("typing "+tree+", pt = "+pt+", undetparams = "+context.undetparams+", implicits-enabled = "+context.implicitsEnabled+", silent = "+context.reportGeneralErrors) //DEBUG
+ printTyping("typing "+tree+", pt = "+pt+", undetparams = "+context.undetparams+", implicits-enabled = "+context.implicitsEnabled+", silent = "+context.reportGeneralErrors) //DEBUG
var tree1 = if (tree.tpe ne null) tree else typed1(tree, mode, dropExistential(pt))
- if (printTypings) println("typed "+tree1+":"+tree1.tpe+(if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")+", undetparams = "+context.undetparams+", pt = "+pt) //DEBUG
+ printTyping("typed "+tree1+":"+tree1.tpe+(if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")+", undetparams = "+context.undetparams+", pt = "+pt) //DEBUG
tree1.tpe = addAnnotations(tree1, tree1.tpe)
val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree)
- if (printTypings) println("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams) //DEBUG
+ printTyping("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams) //DEBUG
// for (t <- tree1.tpe) assert(t != WildcardType)
// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe)
if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result)
@@ -4126,7 +4154,7 @@ trait Typers { self: Analyzer =>
} catch {
case ex: TypeError =>
tree.tpe = null
- if (printTypings) println("caught "+ex+" in typed: "+tree) //DEBUG
+ printTyping("caught "+ex+" in typed: "+tree) //DEBUG
reportTypeError(tree.pos, ex)
setError(tree)
case ex: Exception =>
@@ -4138,6 +4166,7 @@ trait Typers { self: Analyzer =>
throw ex
}
finally {
+ deindentTyping()
if (Statistics.enabled) {
val t = currentTime()
microsByType(pendingTreeTypes.head) += ((t - typerTime) / 1000).toInt
diff --git a/src/library/scala/annotation/implicitNotFound.scala b/src/library/scala/annotation/implicitNotFound.scala
new file mode 100644
index 0000000000..5d9b29c5f8
--- /dev/null
+++ b/src/library/scala/annotation/implicitNotFound.scala
@@ -0,0 +1,18 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.annotation
+
+/**
+ * An annotation that specifies the error message that is emitted when the compiler
+ * cannot find an implicit value of the annotated type.
+ *
+ * @author Adriaan Moors
+ * @since 2.8.1
+ */
+final class implicitNotFound(msg: String) extends StaticAnnotation {} \ No newline at end of file
diff --git a/src/library/scala/collection/generic/CanBuildFrom.scala b/src/library/scala/collection/generic/CanBuildFrom.scala
index 79e352690e..4c923dca44 100644
--- a/src/library/scala/collection/generic/CanBuildFrom.scala
+++ b/src/library/scala/collection/generic/CanBuildFrom.scala
@@ -11,7 +11,7 @@ package scala.collection
package generic
import mutable.Builder
-
+import scala.annotation.implicitNotFound
/** A base trait for builder factories.
*
@@ -25,6 +25,7 @@ import mutable.Builder
* @author Adriaan Moors
* @since 2.8
*/
+@implicitNotFound(msg = "Cannot construct a collection of type ${To} with elements of type ${Elem} based on a collection of type ${To}.")
trait CanBuildFrom[-From, -Elem, +To] {
/** Creates a new builder on request of a collection.
diff --git a/test/files/neg/bug1275.check b/test/files/neg/bug1275.check
index 9f806c0689..6ee8365796 100644
--- a/test/files/neg/bug1275.check
+++ b/test/files/neg/bug1275.check
@@ -1,4 +1,6 @@
-bug1275.scala:13: error: The kind of type MyType does not conform to the expected kind of type MyType[+t] <: TestCovariance.Seq[t] in trait Seq.
- def span[a, s <: Seq[a] { type MyType <: s } ](xs: s): s = xs f
- ^
+bug1275.scala:11: error: type mismatch;
+ found : xs.MyType[a]
+ required: s
+ = xs f
+ ^
one error found
diff --git a/test/files/neg/bug1275.scala b/test/files/neg/bug1275.scala
index e9be13c763..1175b30763 100644
--- a/test/files/neg/bug1275.scala
+++ b/test/files/neg/bug1275.scala
@@ -1,14 +1,15 @@
-// tested using Scala compiler version 2.6.0-RC1 -- (c) 2002-2010 LAMP/EPFL
-
-// prompted by "Covariant return types" mailing list question
-object TestCovariance {
-
- // see Type constructor polymorphism in http://www.scala-lang.org/docu/changelog.html
- trait Seq[+t] {
- type MyType[+t] <: Seq[t]
-
- def f: MyType[t]
- }
-
- def span[a, s <: Seq[a] { type MyType <: s } ](xs: s): s = xs f
-}
+object Test {
+ trait Seq[+t] {
+ type MyType[+t] <: Seq[t]
+
+ def f: MyType[t]
+ }
+
+ // illegal abstract type member refinement: changes the arity of MyType
+ // the error is pretty strange, since the compiler forms the illegal type xs.MyType[a] anyway
+ def span[a, s <: Seq[a] { type MyType/*look ma, no type parameters!*/ <: s } ](xs: s): s
+ = xs f
+// ^
+// found : xs.MyType[a]
+// required: s
+} \ No newline at end of file
diff --git a/test/files/neg/bug882.check b/test/files/neg/bug882.check
index 8f47fefd9b..4e3e6d0860 100644
--- a/test/files/neg/bug882.check
+++ b/test/files/neg/bug882.check
@@ -1,4 +1,4 @@
-bug882.scala:2: error: traits cannot have type parameters with context bounds `: ...'
+bug882.scala:2: error: traits cannot have type parameters with context bounds `: ...' nor view bounds `<% ...'
trait SortedSet[A <% Ordered[A]] {
^
one error found
diff --git a/test/files/neg/t2416.check b/test/files/neg/t2416.check
new file mode 100644
index 0000000000..0899ad09d5
--- /dev/null
+++ b/test/files/neg/t2416.check
@@ -0,0 +1,10 @@
+t2416.scala:3: error: type arguments [Int] do not conform to trait A's type parameter bounds [X <: Double]
+ def x : A[Int]#B = 10 // no you won't
+ ^
+t2416.scala:8: error: type arguments [Boolean] do not conform to type B's type parameter bounds [Y <: Double]
+ def x : A#B[Boolean] = 10 // seriously?
+ ^
+t2416.scala:13: error: type arguments [String] do not conform to type B's type parameter bounds [Z <: Double]
+ type C[Z <: A] = Z#B[String] // nuh-uh!
+ ^
+three errors found
diff --git a/test/files/neg/t2416.scala b/test/files/neg/t2416.scala
new file mode 100644
index 0000000000..6bb57a984b
--- /dev/null
+++ b/test/files/neg/t2416.scala
@@ -0,0 +1,14 @@
+object t2416a {
+ trait A[X <: Double] { type B = X }
+ def x : A[Int]#B = 10 // no you won't
+}
+
+object t2416b {
+ trait A{type B[Y <: Double] = Int}
+ def x : A#B[Boolean] = 10 // seriously?
+}
+
+object t2416c {
+ trait A{type B[Z <: Double] = Int}
+ type C[Z <: A] = Z#B[String] // nuh-uh!
+} \ No newline at end of file
diff --git a/test/files/neg/t2462a.check b/test/files/neg/t2462a.check
new file mode 100644
index 0000000000..040a01f3a1
--- /dev/null
+++ b/test/files/neg/t2462a.check
@@ -0,0 +1,4 @@
+t2462a.scala:2: error: Cannot construct a collection of type List[String] with elements of type Int based on a collection of type List[String].
+ List(1,2,3).map[Int, List[String]](x => 1)
+ ^
+one error found
diff --git a/test/files/neg/t2462a.scala b/test/files/neg/t2462a.scala
new file mode 100644
index 0000000000..2d523b4dd8
--- /dev/null
+++ b/test/files/neg/t2462a.scala
@@ -0,0 +1,3 @@
+object Test {
+ List(1,2,3).map[Int, List[String]](x => 1)
+} \ No newline at end of file
diff --git a/test/files/neg/t2462b.check b/test/files/neg/t2462b.check
new file mode 100644
index 0000000000..bc0d9aa469
--- /dev/null
+++ b/test/files/neg/t2462b.check
@@ -0,0 +1,14 @@
+t2462b.scala:6: warning: Invalid implicitNotFound message for trait Meh in package test:
+The type parameters Too, Elem referenced in the message of the @implicitNotFound annotation are not defined by trait Meh.
+trait Meh[-From, +To]
+ ^
+t2462b.scala:9: warning: Invalid implicitNotFound message for trait Meh2 in package test:
+The type parameter Elem referenced in the message of the @implicitNotFound annotation is not defined by trait Meh2.
+trait Meh2[-From, +To]
+ ^
+t2462b.scala:12: error: overriding method x in class thankyoupartest of type => Int;
+ method x needs `override' modifier
+class testmustfail extends thankyoupartest { def x = 43 }
+ ^
+two warnings found
+one error found
diff --git a/test/files/neg/t2462b.scala b/test/files/neg/t2462b.scala
new file mode 100644
index 0000000000..7a1389cc8e
--- /dev/null
+++ b/test/files/neg/t2462b.scala
@@ -0,0 +1,12 @@
+package test
+
+import scala.annotation.implicitNotFound
+
+@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.")
+trait Meh[-From, +To]
+
+@implicitNotFound(msg = "Cannot construct a collection of type ${To} ${Elem}.")
+trait Meh2[-From, +To]
+
+class thankyoupartest { def x = 42 }
+class testmustfail extends thankyoupartest { def x = 43 }
diff --git a/test/files/neg/t3399.check b/test/files/neg/t3399.check
new file mode 100644
index 0000000000..eb6c679704
--- /dev/null
+++ b/test/files/neg/t3399.check
@@ -0,0 +1,4 @@
+t3399.scala:23: error: could not find implicit value for parameter e: =:=[Nats.Add[Nats._1,Nats._1],Nats._1]
+ implicitly[ Add[_1, _1] =:= _1]
+ ^
+one error found
diff --git a/test/files/neg/t3399.scala b/test/files/neg/t3399.scala
new file mode 100644
index 0000000000..3edaa0724f
--- /dev/null
+++ b/test/files/neg/t3399.scala
@@ -0,0 +1,24 @@
+object Nats {
+ sealed trait Nat {
+ // fold right on N, N-1, ..., 1
+ type FoldR[Init <: Type, Type, F <: Fold[Nat, Type]] <: Type
+ }
+ sealed trait _0 extends Nat {
+ type FoldR[Init <: Type, Type, F <: Fold[Nat, Type]] = Init
+ }
+ sealed trait Succ[N <: Nat] extends Nat {
+ type FoldR[Init <: Type, Type, F <: Fold[Nat, Type]] =
+ F#Apply[Succ[N], N#FoldR[Init, Type, F]]
+ }
+
+ type Add[A <: Nat, B <: Nat] = A#FoldR[B, Nat, Inc]
+ trait Fold[-Elem, Value] {
+ type Apply[N <: Elem, Acc <: Value] <: Value
+ }
+ type Inc = Fold[Any, Nat] {
+ type Apply[N <: Any, Acc <: Nat] = Succ[Acc]
+ }
+
+ type _1 = Succ[_0]
+ implicitly[ Add[_1, _1] =:= _1]
+} \ No newline at end of file
diff --git a/test/files/neg/t3507.check b/test/files/neg/t3507.check
new file mode 100644
index 0000000000..1246a20d09
--- /dev/null
+++ b/test/files/neg/t3507.check
@@ -0,0 +1,4 @@
+t3507.scala:13: error: could not find implicit value for evidence parameter of type Manifest[object _1.b.c]
+ mani/*[object _1.b.c]*/(c) // kaboom in manifestOfType / TreeGen.mkAttributedQualifier
+ ^
+one error found
diff --git a/test/files/neg/t3507.scala b/test/files/neg/t3507.scala
new file mode 100644
index 0000000000..9a8c7c5462
--- /dev/null
+++ b/test/files/neg/t3507.scala
@@ -0,0 +1,15 @@
+class A {
+ object b {
+ object c
+ }
+ def m = b.c
+}
+
+object Test {
+ var a: A = new A // mutable
+ val c /*: object _1.b.c forSome { val _1: A } */ = a.m // widening using existential
+
+ def mani[T: Manifest](x: T) = ()
+ mani/*[object _1.b.c]*/(c) // kaboom in manifestOfType / TreeGen.mkAttributedQualifier
+ // --> _1 is not in scope here
+} \ No newline at end of file
diff --git a/test/files/neg/t3653.check b/test/files/neg/t3653.check
new file mode 100644
index 0000000000..ac6e2ca9dc
--- /dev/null
+++ b/test/files/neg/t3653.check
@@ -0,0 +1,7 @@
+t3653.scala:3: error: double definition:
+method x:(implicit x: Int)Int and
+method x:(i: Int)Int at line 2
+have same type after erasure: (x: Int)Int
+ def x(implicit x: Int) = 5
+ ^
+one error found
diff --git a/test/files/neg/t3653.scala b/test/files/neg/t3653.scala
new file mode 100644
index 0000000000..96cf96008a
--- /dev/null
+++ b/test/files/neg/t3653.scala
@@ -0,0 +1,4 @@
+class B {
+ def x(i: Int) = 3
+ def x(implicit x: Int) = 5
+} \ No newline at end of file
diff --git a/test/files/neg/t3663.check b/test/files/neg/t3663.check
new file mode 100644
index 0000000000..09ea25ad91
--- /dev/null
+++ b/test/files/neg/t3663.check
@@ -0,0 +1,4 @@
+main.scala:11: error: variable foo cannot be accessed in test.Test
+ println(t.foo)
+ ^
+one error found
diff --git a/test/files/neg/t3663/PackageProtected.java b/test/files/neg/t3663/PackageProtected.java
new file mode 100644
index 0000000000..f4535a55b4
--- /dev/null
+++ b/test/files/neg/t3663/PackageProtected.java
@@ -0,0 +1,5 @@
+package test;
+
+class PackageProtected {
+ int foo;
+}
diff --git a/test/files/neg/t3663/main.scala b/test/files/neg/t3663/main.scala
new file mode 100644
index 0000000000..29619550cc
--- /dev/null
+++ b/test/files/neg/t3663/main.scala
@@ -0,0 +1,14 @@
+package test
+
+final class Test extends PackageProtected {
+ def bar = foo
+}
+
+package another {
+ object Main {
+ def bug(t: Test) {
+ // Can always be replicated.
+ println(t.foo)
+ }
+ }
+} \ No newline at end of file
diff --git a/test/files/neg/t3691.check b/test/files/neg/t3691.check
new file mode 100644
index 0000000000..1b548cc84d
--- /dev/null
+++ b/test/files/neg/t3691.check
@@ -0,0 +1,16 @@
+t3691.scala:4: error: type mismatch;
+ found : java.lang.Object with Test.A[String]
+ required: AnyRef{type A[x]}
+ val b = (new A[String]{}): { type A[x] } // not ok
+ ^
+t3691.scala:5: error: type mismatch;
+ found : java.lang.Object with Test.A[String]
+ required: AnyRef{type A}
+ val c = (new A[String]{}): { type A } // not ok
+ ^
+t3691.scala:7: error: type mismatch;
+ found : java.lang.Object{type A = String}
+ required: AnyRef{type A[X]}
+ val x = (new { type A = String }): { type A[X] } // not ok
+ ^
+three errors found
diff --git a/test/files/neg/t3691.scala b/test/files/neg/t3691.scala
new file mode 100644
index 0000000000..69e8bef630
--- /dev/null
+++ b/test/files/neg/t3691.scala
@@ -0,0 +1,11 @@
+object Test {
+ trait A[X] { type A[x <: X] = x }
+ val a = (new A[String]{}): { type A[x <: String] } // ok
+ val b = (new A[String]{}): { type A[x] } // not ok
+ val c = (new A[String]{}): { type A } // not ok
+
+ val x = (new { type A = String }): { type A[X] } // not ok
+//a: AnyRef{type A[X]}
+
+ identity[x.A[Any]] _
+} \ No newline at end of file
diff --git a/test/files/neg/t742.check b/test/files/neg/t742.check
new file mode 100644
index 0000000000..f587948ef1
--- /dev/null
+++ b/test/files/neg/t742.check
@@ -0,0 +1,5 @@
+t742.scala:5: error: kinds of the type arguments (Crash._1,Crash._2,Any) do not conform to the expected kinds of the type parameters (type m,type n,type z).
+Crash._1's type parameters do not match type m's expected parameters: type s1 has one type parameter, but type n has two
+ type p = mul[_1, _2, Any] // mul[_1, _1, Any] needs -Yrecursion
+ ^
+one error found
diff --git a/test/files/neg/t742.scala b/test/files/neg/t742.scala
new file mode 100644
index 0000000000..bb1c2f85ea
--- /dev/null
+++ b/test/files/neg/t742.scala
@@ -0,0 +1,8 @@
+object Crash {
+ type mul[m[n[s[_], z], z], n[s[_], z], z] = m[n, z]
+ type _1[s1[_], z1] = s1[z1]
+ type _2[s1[_], z1] = s1[z1]
+ type p = mul[_1, _2, Any] // mul[_1, _1, Any] needs -Yrecursion
+ // _1[_2, Zero]
+ // _2[Zero]
+} \ No newline at end of file
diff --git a/test/files/pos/nothing_manifest_disambig.scala b/test/files/pos/nothing_manifest_disambig.scala
new file mode 100644
index 0000000000..9a3db0c6d4
--- /dev/null
+++ b/test/files/pos/nothing_manifest_disambig.scala
@@ -0,0 +1,10 @@
+object Test {
+ def mani[T: Manifest](xs: T) = xs
+ mani(List())
+
+ def listElMani[T: Manifest](xs: List[T]) = xs
+ listElMani(List())
+
+ def foo[A, C](m : C)(implicit ev: C <:< Traversable[A], mani: Manifest[A]): (C, A, Manifest[A]) = (m, m.head, mani)
+ foo(List(1,2,3))
+} \ No newline at end of file
diff --git a/test/files/pos/t2331.scala b/test/files/pos/t2331.scala
new file mode 100644
index 0000000000..9a15b5c2a9
--- /dev/null
+++ b/test/files/pos/t2331.scala
@@ -0,0 +1,11 @@
+trait C {
+ def m[T]: T
+}
+
+object Test {
+ val o /*: C --> no crash*/ = new C {
+ def m[T]: Nothing /*: T --> no crash*/ = error("omitted")
+ }
+
+ o.m[Nothing]
+} \ No newline at end of file
diff --git a/test/files/pos/t3249/Test.java b/test/files/pos/t3249/Test.java
new file mode 100644
index 0000000000..4cc7cb2ab5
--- /dev/null
+++ b/test/files/pos/t3249/Test.java
@@ -0,0 +1,5 @@
+public class Test {
+ public static void meh() {
+ new A<Integer>().f();
+ }
+} \ No newline at end of file
diff --git a/test/files/pos/t3249/a.scala b/test/files/pos/t3249/a.scala
new file mode 100644
index 0000000000..0394464549
--- /dev/null
+++ b/test/files/pos/t3249/a.scala
@@ -0,0 +1,11 @@
+class A[U] { def f[T] = { class X extends A[T] } }
+
+
+/*
+$ scalac a.scala
+$ javac -cp .:$SCALA_HOME/lib/scala-library.jar -Xprint 'A$X$1'
+
+ public class X$1 extends A<java.lang.Object> implements scala.ScalaObject {
+ public X$1(A<U> null);
+ }
+*/ \ No newline at end of file
diff --git a/test/files/pos/t3374.scala b/test/files/pos/t3374.scala
new file mode 100644
index 0000000000..4c0293181d
--- /dev/null
+++ b/test/files/pos/t3374.scala
@@ -0,0 +1,6 @@
+trait Parent {
+ type Test[A, H[B <: A]]
+}
+trait Sub extends Parent {
+ type Test[AS, HS[B <: AS]] = AS
+} \ No newline at end of file
diff --git a/test/files/pos/t3419/B_1.scala b/test/files/pos/t3419/B_1.scala
new file mode 100644
index 0000000000..a8ec7edba4
--- /dev/null
+++ b/test/files/pos/t3419/B_1.scala
@@ -0,0 +1,3 @@
+trait T[A,B] {
+ type X[a <: A, b <: B] <: B
+} \ No newline at end of file
diff --git a/test/files/pos/t3419/C_2.scala b/test/files/pos/t3419/C_2.scala
new file mode 100644
index 0000000000..da721d2c31
--- /dev/null
+++ b/test/files/pos/t3419/C_2.scala
@@ -0,0 +1,3 @@
+object F {
+ type S = T[Any,Int] {type X[N <: Int, Acc <: Int] = Acc}
+} \ No newline at end of file
diff --git a/test/files/pos/t3477.scala b/test/files/pos/t3477.scala
new file mode 100644
index 0000000000..660aa55736
--- /dev/null
+++ b/test/files/pos/t3477.scala
@@ -0,0 +1,7 @@
+class J3 {
+ def f[K, K1 >: K, V](x: Map[K1, V]): Map[K, V] = error("")
+}
+
+object Test {
+ (new J3).f(Map[Int, Int]())
+} \ No newline at end of file
diff --git a/test/files/pos/t3486/JTest.java b/test/files/pos/t3486/JTest.java
new file mode 100644
index 0000000000..0bf388b72d
--- /dev/null
+++ b/test/files/pos/t3486/JTest.java
@@ -0,0 +1,3 @@
+public class JTest<A> extends T2<A> {
+ public A m( A a ) { return a; }
+} \ No newline at end of file
diff --git a/test/files/pos/t3486/test.scala b/test/files/pos/t3486/test.scala
new file mode 100644
index 0000000000..544232b0d1
--- /dev/null
+++ b/test/files/pos/t3486/test.scala
@@ -0,0 +1,6 @@
+trait Test[A] {
+ def m( a: A ): A
+ def specified(a:A):A = a
+}
+
+abstract class T2[A] extends Test[A] \ No newline at end of file
diff --git a/test/files/pos/t3494.scala b/test/files/pos/t3494.scala
new file mode 100644
index 0000000000..35a4bcde5d
--- /dev/null
+++ b/test/files/pos/t3494.scala
@@ -0,0 +1,7 @@
+object Test {
+ def f[T](xs: T*) = ()
+
+ val x = "abc"
+
+ f[x.type](x)
+} \ No newline at end of file
diff --git a/test/files/pos/t3582.scala b/test/files/pos/t3582.scala
new file mode 100644
index 0000000000..0ac112efbf
--- /dev/null
+++ b/test/files/pos/t3582.scala
@@ -0,0 +1,12 @@
+trait C[A]
+object Test {
+ def ImplicitParamCA[CC[A], A](implicit ev: C[A]) {implicitly[C[A]]} // must use this exact syntax...
+ // error: could not find implicit value for parameter e: C[A]
+}
+// [[syntax trees at end of typer]]
+// abstract trait C#5[A#9116 >: Nothing#5832 <: Any#52] extends scala#33.AnyRef#2780;
+// final object Test#15 extends java.lang.Object#2485 with ScalaObject#1913 {
+// def ImplicitParamCA#9123[CC#9124[A#10858 >: Nothing#5832 <: Any#52] >: [A#10858]Nothing#5832 <: [A#10858]Any#52,
+// A#9125 >: Nothing#5832 <: Any#52](implicit ev#10856: C#5[A#9127]): Unit#3818
+// = scala#34.this.Predef#1683.implicitly#8816[C#5[A#10858]]()
+// }
diff --git a/test/files/pos/t3582b.scala b/test/files/pos/t3582b.scala
new file mode 100644
index 0000000000..8f0bfb9b2a
--- /dev/null
+++ b/test/files/pos/t3582b.scala
@@ -0,0 +1,5 @@
+object ParamScoping {
+ // scoping worked fine in the result type, but was wrong in body
+ // reason: typedTypeDef needs new context, which was set up by typed1 but not by typedDefDef and typedClassDef
+ def noOverlapFOwithHO[T, G[T]]: G[T] = null.asInstanceOf[G[T]]
+} \ No newline at end of file
diff --git a/test/files/pos/t3622/test/AsyncTask.java b/test/files/pos/t3622/test/AsyncTask.java
new file mode 100644
index 0000000000..cfcea3fe1a
--- /dev/null
+++ b/test/files/pos/t3622/test/AsyncTask.java
@@ -0,0 +1,5 @@
+package test;
+
+public abstract class AsyncTask<Params, Progress, Result> {
+ protected abstract Result doInBackground(Params... args);
+} \ No newline at end of file
diff --git a/test/files/pos/t3622/test/MyAsyncTask.java b/test/files/pos/t3622/test/MyAsyncTask.java
new file mode 100644
index 0000000000..9ef4947052
--- /dev/null
+++ b/test/files/pos/t3622/test/MyAsyncTask.java
@@ -0,0 +1,9 @@
+package test;
+
+public abstract class MyAsyncTask extends AsyncTask<String, String, String> {
+ protected abstract String doInBackground1(String[] args);
+ @Override
+ protected String doInBackground(String... args) {
+ return doInBackground1(new String[]{"dummy"});
+ }
+} \ No newline at end of file
diff --git a/test/files/pos/t3622/test/Test.scala b/test/files/pos/t3622/test/Test.scala
new file mode 100644
index 0000000000..fb82c581f9
--- /dev/null
+++ b/test/files/pos/t3622/test/Test.scala
@@ -0,0 +1,5 @@
+package test
+
+class Test extends MyAsyncTask {
+ protected[test] def doInBackground1(args: Array[String]): String = ""
+} \ No newline at end of file
diff --git a/test/files/pos/t3676.scala b/test/files/pos/t3676.scala
new file mode 100644
index 0000000000..60c0ceaec8
--- /dev/null
+++ b/test/files/pos/t3676.scala
@@ -0,0 +1,5 @@
+trait SeqLike[+Repr]
+trait Seq extends SeqLike[Seq]
+
+trait MySeq extends Seq with SeqLike[MySub]
+trait MySub extends MySeq
diff --git a/test/files/pos/t3777.scala b/test/files/pos/t3777.scala
new file mode 100644
index 0000000000..165eeebfdb
--- /dev/null
+++ b/test/files/pos/t3777.scala
@@ -0,0 +1,7 @@
+object Test {
+ type Point = Map[Symbol, String]
+ type Points = IndexedSeq[Point]
+
+ def makePoints2: Points = IndexedSeq[Point]()
+ val spoints2 = util.Random.shuffle(makePoints2)
+}