diff options
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) +} |