From fb0634f84b77bafd87831da7e88fda1f4e23c010 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Mar 2014 22:06:13 +0100 Subject: [PATCH 1/3] New scheme for TypeTrees that refer to others Adds a new scheme by which a TypeTree() can refer for its type to the symbol of some other type. Applies the scheme to setter parameters, replacing the previous ad-hoc solution. --- src/dotty/tools/dotc/ast/Desugar.scala | 42 +++++++++++++++++++++-- src/dotty/tools/dotc/typer/Checking.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 12 ++++--- src/dotty/tools/dotc/typer/Typer.scala | 26 +++++++++++--- 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 027c3238d91c..9f00ad0602c5 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -6,6 +6,7 @@ import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ import Decorators._ +import util.Attachment import language.higherKinds import collection.mutable.ListBuffer import typer.ErrorReporting.InfoString @@ -21,6 +22,43 @@ object desugar { /** Info of a variable in a pattern: The named tree and its type */ private type VarInfo = (NameTree, Tree) +// ----- TypeTrees that refer to other tree's symbols ------------------- + + /** A marker tree used as the original for TypeTrees that get their type by taking + * the typeRef of some other tree's symbol. (currently unused) + */ + val TypeRefOfSym = new TypeTree(EmptyTree) + + /** A marker tree used as the original for TypeTrees that get their type by taking + * the result type of the info of some other tree's symbol. + */ + val InfoOfSym = new TypeTree(EmptyTree) + + /** Attachment key containing TypeTrees whose type is computed + * from the symbol in this type. These type trees have marker trees + * TypeRefOfSym or InfoOfSym as their originals. + */ + val References = new Attachment.Key[List[Tree]] + + /** Attachment key for TypeTrees marked with TypeRefOfSym or InfoOfSym + * which contains the symbol of the original tree from which this + * TypeTree is derived. + */ + val OriginalSymbol = new Attachment.Key[Symbol] + + /** A type tree that is marked to get its type by taking + * the typeRef of some other tree's symbol. Enters the type tree + * in the References attachment of the `original` tree as a side effect. + */ + def refTypeTree(original: Tree, marker: TypeTree): TypeTree = { + val result = TypeTree(marker) + val existing = original.attachmentOrElse(References, Nil) + original.putAttachment(References, result :: existing) + result + } + +// ----- Desugar methods ------------------------------------------------- + /** var x: Int = expr * ==> * def x: Int = expr @@ -35,7 +73,7 @@ object desugar { // val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ? // right now vdef maps via expandedTree to a thicket which concerns itself. // I don't see a problem with that but if there is one we can avoid it by making a copy here. - val setterParam = makeSyntheticParameter(tpt = TypeTree()) + val setterParam = makeSyntheticParameter(tpt = refTypeTree(vdef, InfoOfSym)) val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral val setter = cpy.DefDef(vdef, mods | Accessor, name.setterName, Nil, (setterParam :: Nil) :: Nil, @@ -180,7 +218,7 @@ object desugar { constr1.mods, constr1.name, tparams, vparamss, constr1.tpt, constr1.rhs) // a reference to the class type, with all parameters given. - val classTypeRef: Tree = { + lazy val classTypeRef: Tree = { // Dotty deviation: Without type annotation infers Ident | AppliedTypeTree, which // renders the :\ in companions below untypable. val tycon = Ident(cdef.name) withPos cdef.pos.startPos diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 39da1dbaeaa8..ea396519ad2a 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -84,7 +84,7 @@ trait Checking extends NoChecking { /** Check that (return) type of implicit definition is not empty */ override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { - case TypeTree(original) if original.isEmpty => + case TypeTree(untpd.EmptyTree) => val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) case _ => diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 2c5022726098..eecc5b286339 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -173,6 +173,11 @@ class Namer { typer: Typer => enclosingClassNamed(mods.privateWithin, mods.pos) def record(sym: Symbol): Symbol = { + val refs = tree.attachmentOrElse(desugar.References, Nil) + if (refs.nonEmpty) { + tree.removeAttachment(desugar.References) + refs foreach (_.pushAttachment(desugar.OriginalSymbol, sym)) + } tree.pushAttachment(SymOfTree, sym) sym } @@ -516,10 +521,7 @@ class Namer { typer: Typer => * NoType if neither case holds. */ val inherited = - if ((sym is Param) && sym.owner.isSetter) // fill in type from getter result type - defContext(sym.owner) - .denotNamed(sym.owner.asTerm.name.setterToGetter).info.widenExpr - else if (sym.owner.isTerm) NoType + if (sym.owner.isTerm) NoType else { // TODO: Look only at member of supertype instead? lazy val schema = paramFn(WildcardType) @@ -578,7 +580,7 @@ class Namer { typer: Typer => val rhsCtx = ctx.fresh addMode Mode.InferringReturnType def rhsType = typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos) - inherited orElse lhsType + inherited orElse lhsType orElse WildcardType } paramFn(typedAheadType(mdef.tpt, pt).tpe) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index c17c9d6d704e..73dc26a91814 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -635,11 +635,27 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { - val original1 = typed(tree.original) - val ownType = - if (original1.isEmpty) { assert(isFullyDefined(pt, ForceDegree.none)); pt } - else original1.tpe - cpy.TypeTree(tree, original1) withType ownType + if (tree.original.isEmpty) { + def symbol = tree.attachment(desugar.OriginalSymbol) + // btw, no need to remove the attachment. The typed + // tree is different from the untyped one, so the + // untyped tree is no longer accessed after all + // accesses with typedTypeTree are done. + val ownType = tree.original match { + case untpd.EmptyTree => + assert(isFullyDefined(pt, ForceDegree.none)) + pt + case desugar.TypeRefOfSym => + symbol.typeRef + case desugar.InfoOfSym => + symbol.info.resultType + } + cpy.TypeTree(tree, untpd.EmptyTree).withType(ownType) + } + else { + val original1 = typed(tree.original) + cpy.TypeTree(tree, original1).withType(original1.tpe) + } } def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { From 51785a8739f06d7f1d3375a19cec441f1f8008e3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Mar 2014 22:42:39 +0100 Subject: [PATCH 2/3] Generalize derived type tree scheme. To get truly hygienic desugared trees, we need a derived type tree scheme that's more flexible than just the previous two choices of info-of-symbol and typeref-of-symbol. The new scheme based on DerivedTypeTrees allows arbitrary methods to derive the type tree's type. --- src/dotty/tools/dotc/ast/Desugar.scala | 32 +++++++++++--------------- src/dotty/tools/dotc/typer/Namer.scala | 2 ++ src/dotty/tools/dotc/typer/Typer.scala | 25 ++++++++------------ 3 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 9f00ad0602c5..659bdd7e5582 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -24,16 +24,6 @@ object desugar { // ----- TypeTrees that refer to other tree's symbols ------------------- - /** A marker tree used as the original for TypeTrees that get their type by taking - * the typeRef of some other tree's symbol. (currently unused) - */ - val TypeRefOfSym = new TypeTree(EmptyTree) - - /** A marker tree used as the original for TypeTrees that get their type by taking - * the result type of the info of some other tree's symbol. - */ - val InfoOfSym = new TypeTree(EmptyTree) - /** Attachment key containing TypeTrees whose type is computed * from the symbol in this type. These type trees have marker trees * TypeRefOfSym or InfoOfSym as their originals. @@ -46,15 +36,19 @@ object desugar { */ val OriginalSymbol = new Attachment.Key[Symbol] - /** A type tree that is marked to get its type by taking - * the typeRef of some other tree's symbol. Enters the type tree - * in the References attachment of the `original` tree as a side effect. + /** A type tree that gets its type from some other tree's symbol. Enters the + * type tree in the References attachment of the `from` tree as a side effect. */ - def refTypeTree(original: Tree, marker: TypeTree): TypeTree = { - val result = TypeTree(marker) - val existing = original.attachmentOrElse(References, Nil) - original.putAttachment(References, result :: existing) - result + abstract class DerivedTypeTree(from: Tree) extends TypeTree(EmptyTree) { + val existing = from.attachmentOrElse(References, Nil) + from.putAttachment(References, this :: existing) + + /** The method that computes the type of this tree */ + def derivedType(originalSym: Symbol)(implicit ctx: Context): Type + } + + class SetterParam(vdef: ValDef) extends DerivedTypeTree(vdef) { + def derivedType(vsym: Symbol)(implicit ctx: Context) = vsym.info.resultType } // ----- Desugar methods ------------------------------------------------- @@ -73,7 +67,7 @@ object desugar { // val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ? // right now vdef maps via expandedTree to a thicket which concerns itself. // I don't see a problem with that but if there is one we can avoid it by making a copy here. - val setterParam = makeSyntheticParameter(tpt = refTypeTree(vdef, InfoOfSym)) + val setterParam = makeSyntheticParameter(tpt = new SetterParam(vdef)) val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral val setter = cpy.DefDef(vdef, mods | Accessor, name.setterName, Nil, (setterParam :: Nil) :: Nil, diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index eecc5b286339..dadb0f9f8832 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -458,6 +458,8 @@ class Namer { typer: Typer => index(constr) index(rest)(inClassContext(selfInfo)) denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo) + // make sure constr parameters are all entered because we refer to them in desugarings: + symbolOfTree(constr).ensureCompleted() } } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 73dc26a91814..f7e47e4bf793 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -635,23 +635,18 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { - if (tree.original.isEmpty) { - def symbol = tree.attachment(desugar.OriginalSymbol) - // btw, no need to remove the attachment. The typed - // tree is different from the untyped one, so the - // untyped tree is no longer accessed after all - // accesses with typedTypeTree are done. - val ownType = tree.original match { - case untpd.EmptyTree => + if (tree.original.isEmpty) + tree match { + case tree: desugar.DerivedTypeTree => + TypeTree(tree.derivedType(tree.attachment(desugar.OriginalSymbol))) withPos tree.pos + // btw, no need to remove the attachment. The typed + // tree is different from the untyped one, so the + // untyped tree is no longer accessed after all + // accesses with typedTypeTree are done. + case _ => assert(isFullyDefined(pt, ForceDegree.none)) - pt - case desugar.TypeRefOfSym => - symbol.typeRef - case desugar.InfoOfSym => - symbol.info.resultType + tree.withType(pt) } - cpy.TypeTree(tree, untpd.EmptyTree).withType(ownType) - } else { val original1 = typed(tree.original) cpy.TypeTree(tree, original1).withType(original1.tpe) From be373269c921d7dba8ea0ede27c9067a0cd0fea8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 12 Mar 2014 15:06:17 +0100 Subject: [PATCH 3/3] Hygienic desugaring Made desugaring hygienic. Trees that are derived from some other tree are no longer stored as simple untyped Ident trees, but as TypeTrees that know how to derive their types from some other type. Test cases in pos: hygiene.scala, t0054.scala and t0085.scala. The comment in hygiene.scala points to the difficulties we are facing. In particular, we need type trees that can rebind some references of a source type to local occurrences with the same name. t0054.scala is similar to hygiene.scala. t0085.scala is trickier, but also related. Essentially the problem there is that we have a class that inherits its outer class. In this case it is important to resolve an identifier in the right context. The identifier added to the copy method of a case class must be resolved outside the class (just like the same identifier in the constructor of that case class). --- src/dotty/tools/dotc/ast/Desugar.scala | 172 ++++++++++++------ src/dotty/tools/dotc/ast/untpd.scala | 44 ++++- .../tools/dotc/core/SymDenotations.scala | 1 + .../tools/dotc/printing/RefinedPrinter.scala | 2 + src/dotty/tools/dotc/typer/Checking.scala | 1 + src/dotty/tools/dotc/typer/Namer.scala | 34 ++-- src/dotty/tools/dotc/typer/Typer.scala | 19 +- test/dotc/tests.scala | 2 + tests/pos/A.scala | 8 + tests/pos/hygiene.scala | 28 +++ tests/pos/t0017.scala | 21 +++ tests/pos/t0020.scala | 8 + tests/pos/t0029.scala | 3 + tests/pos/t0030.scala | 9 + tests/pos/t0031.scala | 29 +++ tests/pos/t0032.scala | 17 ++ tests/pos/t0036.scala | 8 + tests/{new => pos}/t0039.scala | 0 tests/pos/t0049.scala | 3 + tests/pos/t0053.scala | 7 + tests/pos/t0055.scala | 6 + tests/pos/t0061.scala | 10 + tests/pos/t0064.scala | 6 + tests/pos/t0066.scala | 7 + tests/pos/t0068.scala | 6 + tests/pos/t0069.scala | 10 + tests/pos/t0076.scala | 9 + tests/pos/t0081.scala | 4 + tests/pos/t0082.scala | 18 ++ tests/pos/t0085.scala | 8 + tests/pos/t0091.scala | 6 + tests/pos/t0093.scala | 4 + tests/pos/t0095.scala | 15 ++ 33 files changed, 448 insertions(+), 77 deletions(-) create mode 100644 tests/pos/A.scala create mode 100644 tests/pos/hygiene.scala create mode 100644 tests/pos/t0017.scala create mode 100644 tests/pos/t0020.scala create mode 100644 tests/pos/t0029.scala create mode 100644 tests/pos/t0030.scala create mode 100644 tests/pos/t0031.scala create mode 100644 tests/pos/t0032.scala create mode 100644 tests/pos/t0036.scala rename tests/{new => pos}/t0039.scala (100%) create mode 100644 tests/pos/t0049.scala create mode 100644 tests/pos/t0053.scala create mode 100644 tests/pos/t0055.scala create mode 100644 tests/pos/t0061.scala create mode 100644 tests/pos/t0064.scala create mode 100644 tests/pos/t0066.scala create mode 100644 tests/pos/t0068.scala create mode 100644 tests/pos/t0069.scala create mode 100644 tests/pos/t0076.scala create mode 100644 tests/pos/t0081.scala create mode 100644 tests/pos/t0082.scala create mode 100644 tests/pos/t0085.scala create mode 100644 tests/pos/t0091.scala create mode 100644 tests/pos/t0093.scala create mode 100644 tests/pos/t0095.scala diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 659bdd7e5582..dc38e99b5c6f 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -6,9 +6,9 @@ import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ import Decorators._ -import util.Attachment import language.higherKinds import collection.mutable.ListBuffer +import config.Printers._ import typer.ErrorReporting.InfoString import typer.Mode @@ -22,34 +22,64 @@ object desugar { /** Info of a variable in a pattern: The named tree and its type */ private type VarInfo = (NameTree, Tree) -// ----- TypeTrees that refer to other tree's symbols ------------------- +// ----- DerivedTypeTrees ----------------------------------- - /** Attachment key containing TypeTrees whose type is computed - * from the symbol in this type. These type trees have marker trees - * TypeRefOfSym or InfoOfSym as their originals. - */ - val References = new Attachment.Key[List[Tree]] + class SetterParam extends DerivedTypeTree { + def derivedType(sym: Symbol)(implicit ctx: Context) = sym.info.resultType + } - /** Attachment key for TypeTrees marked with TypeRefOfSym or InfoOfSym - * which contains the symbol of the original tree from which this - * TypeTree is derived. - */ - val OriginalSymbol = new Attachment.Key[Symbol] + class TypeRefTree extends DerivedTypeTree { + def derivedType(sym: Symbol)(implicit ctx: Context) = sym.typeRef + } - /** A type tree that gets its type from some other tree's symbol. Enters the - * type tree in the References attachment of the `from` tree as a side effect. - */ - abstract class DerivedTypeTree(from: Tree) extends TypeTree(EmptyTree) { - val existing = from.attachmentOrElse(References, Nil) - from.putAttachment(References, this :: existing) + class DerivedFromParamTree extends DerivedTypeTree { - /** The method that computes the type of this tree */ - def derivedType(originalSym: Symbol)(implicit ctx: Context): Type + /** Make sure that for all enclosing module classes their companion lasses + * are completed. Reason: We need the constructor of such companion classes to + * be completed so that OriginalSymbol attachments are pushed to DerivedTypeTrees + * in appy/unapply methods. + */ + override def ensureCompletions(implicit ctx: Context) = + if (!(ctx.owner is Package)) + if (ctx.owner is ModuleClass) ctx.owner.linkedClass.ensureCompleted() + else ensureCompletions(ctx.outer) + + /** Return info of original symbol, where all references to siblings of the + * original symbol (i.e. sibling and original symbol have the same owner) + * are rewired to same-named parameters or accessors in the scope enclosing + * the current scope. The current scope is the scope owned by the defined symbol + * itself, that's why we have to look one scope further out. If the resulting + * type is an alias type, dealias it. This is necessary because the + * accessor of a type parameter is a private type alias that cannot be accessed + * from subclasses. + */ + def derivedType(sym: Symbol)(implicit ctx: Context) = { + val relocate = new TypeMap { + val originalOwner = sym.owner + def apply(tp: Type) = tp match { + case tp: NamedType if tp.symbol.owner eq originalOwner => + val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next + var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol + typr.println(s"rewiring ${tp.symbol} from ${originalOwner.showLocated} to ${local.showLocated}, current owner = ${ctx.owner.showLocated}") + if (local.exists) (defctx.owner.thisType select local).dealias + else throw new Error(s"no matching symbol for ${sym.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}") + case _ => + mapOver(tp) + } + } + relocate(sym.info) + } } - class SetterParam(vdef: ValDef) extends DerivedTypeTree(vdef) { - def derivedType(vsym: Symbol)(implicit ctx: Context) = vsym.info.resultType - } + /** A type definition copied from `tdef` with a rhs typetree derived from it */ + def derivedTypeParam(tdef: TypeDef) = + cpy.TypeDef(tdef, tdef.mods, tdef.name, + new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef, tdef.tparams) // todo: copy type params + + /** A value definition copied from `vdef` with a tpt typetree derived from it */ + def derivedTermParam(vdef: ValDef) = + cpy.ValDef(vdef, vdef.mods, vdef.name, + new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef, vdef.rhs) // ----- Desugar methods ------------------------------------------------- @@ -67,7 +97,7 @@ object desugar { // val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ? // right now vdef maps via expandedTree to a thicket which concerns itself. // I don't see a problem with that but if there is one we can avoid it by making a copy here. - val setterParam = makeSyntheticParameter(tpt = new SetterParam(vdef)) + val setterParam = makeSyntheticParameter(tpt = (new SetterParam).watching(vdef)) val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral val setter = cpy.DefDef(vdef, mods | Accessor, name.setterName, Nil, (setterParam :: Nil) :: Nil, @@ -183,6 +213,9 @@ object desugar { private def toDefParam(tparam: TypeDef) = cpy.TypeDef(tparam, Modifiers(Param), tparam.name, tparam.rhs, tparam.tparams) + private def toDefParam(vparam: ValDef) = + cpy.ValDef(vparam, Modifiers(Param | vparam.mods.flags & Implicit), vparam.name, vparam.tpt, vparam.rhs) + /** The expansion of a class definition. See inline comments for what is involved */ def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = { val TypeDef( @@ -198,30 +231,34 @@ object desugar { // prefixed by type or val). `tparams` and `vparamss` are the type parameters that // go in `constr`, the constructor after desugaring. - val tparams = constr1.tparams map toDefParam - val vparamss = + val constrTparams = constr1.tparams map toDefParam + val constrVparamss = if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty if (mods is Case) ctx.error("case class needs to have at least one parameter list", cdef.pos) ListOfNil - } else - constr1.vparamss.nestedMap(vparam => cpy.ValDef(vparam, - Modifiers(Param | vparam.mods.flags & Implicit), vparam.name, vparam.tpt, vparam.rhs)) - + } + else constr1.vparamss.nestedMap(toDefParam) val constr = cpy.DefDef(constr1, - constr1.mods, constr1.name, tparams, vparamss, constr1.tpt, constr1.rhs) + constr1.mods, constr1.name, constrTparams, constrVparamss, constr1.tpt, constr1.rhs) + + val derivedTparams = constrTparams map derivedTypeParam + val derivedVparamss = constrVparamss nestedMap derivedTermParam + val arity = constrVparamss.head.length + + var classTycon: Tree = EmptyTree // a reference to the class type, with all parameters given. lazy val classTypeRef: Tree = { // Dotty deviation: Without type annotation infers Ident | AppliedTypeTree, which // renders the :\ in companions below untypable. - val tycon = Ident(cdef.name) withPos cdef.pos.startPos + classTycon = (new TypeRefTree) withPos cdef.pos.startPos // watching is set at end of method val tparams = impl.constr.tparams - if (tparams.isEmpty) tycon else AppliedTypeTree(tycon, tparams map refOfDef) + if (tparams.isEmpty) classTycon else AppliedTypeTree(classTycon, tparams map refOfDef) } // new C[Ts](paramss) - lazy val creatorExpr = New(classTypeRef, vparamss nestedMap refOfDef) + lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef) // Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams) // def isDefined = true @@ -232,23 +269,23 @@ object desugar { // def copy(p1: T1 = p1, ..., pN: TN = pN)(moreParams) = new C[...](p1, ..., pN)(moreParams) val caseClassMeths = if (mods is Case) { - val caseParams = vparamss.head.toArray def syntheticProperty(name: TermName, rhs: Tree) = DefDef(synthetic, name, Nil, Nil, TypeTree(), rhs) val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true))) - val productArityMeth = syntheticProperty(nme.productArity, Literal(Constant(caseParams.length))) + val productArityMeth = syntheticProperty(nme.productArity, Literal(Constant(arity))) def selectorName(n: Int) = - if (caseParams.length == 1) nme.get else nme.selectorName(n) - val productElemMeths = for (i <- 0 until caseParams.length) yield + if (arity == 1) nme.get else nme.selectorName(n) + val caseParams = constrVparamss.head.toArray + val productElemMeths = for (i <- 0 until arity) yield syntheticProperty(selectorName(i), Select(This(EmptyTypeName), caseParams(i).name)) val copyMeths = if (mods is Abstract) Nil else { - val copyFirstParams = vparamss.head.map(vparam => + val copyFirstParams = derivedVparamss.head.map(vparam => cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, refOfDef(vparam))) - val copyRestParamss = vparamss.tail.nestedMap(vparam => + val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, EmptyTree)) - DefDef(synthetic, nme.copy, tparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) :: Nil + DefDef(synthetic, nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) :: Nil } copyMeths ::: isDefinedMeth :: productArityMeth :: productElemMeths.toList } @@ -257,15 +294,14 @@ object desugar { def anyRef = ref(defn.AnyRefAlias.typeRef) def productConstr(n: Int) = { val tycon = ref(defn.ProductNClass(n).typeRef) - val targs = vparamss.head map (_.tpt) + val targs = constrVparamss.head map (_.tpt) AppliedTypeTree(tycon, targs) } // Case classes get a ProductN parent var parents1 = parents - val n = vparamss.head.length - if ((mods is Case) && 2 <= n && n <= Definitions.MaxTupleArity) - parents1 = parents1 :+ productConstr(n) + if ((mods is Case) && 2 <= arity && arity <= Definitions.MaxTupleArity) + parents1 = parents1 :+ productConstr(arity) // The thicket which is the desugared version of the companion object // synthetic object C extends parentTpt { defs } @@ -287,17 +323,18 @@ object desugar { val companions = if (mods is Case) { val parent = - if (tparams.nonEmpty) anyRef - else (vparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe)) + if (constrTparams.nonEmpty) anyRef // todo: also use anyRef if constructor has a dependent method type (or rule that out)! + else (constrVparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe)) val applyMeths = if (mods is Abstract) Nil - else DefDef( + else + DefDef( synthetic | (constr1.mods.flags & DefaultParameterized), nme.apply, - tparams, vparamss, TypeTree(), creatorExpr) :: Nil + derivedTparams, derivedVparamss, TypeTree(), creatorExpr) :: Nil val unapplyMeth = { val unapplyParam = makeSyntheticParameter(tpt = classTypeRef) - val unapplyRHS = if (n == 0) Literal(Constant(true)) else Ident(unapplyParam.name) - DefDef(synthetic, nme.unapply, tparams, (unapplyParam :: Nil) :: Nil, TypeTree(), unapplyRHS) + val unapplyRHS = if (arity == 0) Literal(Constant(true)) else Ident(unapplyParam.name) + DefDef(synthetic, nme.unapply, derivedTparams, (unapplyParam :: Nil) :: Nil, TypeTree(), unapplyRHS) } companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters) } @@ -314,19 +351,40 @@ object desugar { ctx.error("implicit classes may not be toplevel", cdef.pos) if (mods is Case) ctx.error("implicit classes may not case classes", cdef.pos) + + // implicit wrapper is typechecked in same scope as constructor, so + // we can reuse the constructor parameters; no derived params are needed. DefDef(Modifiers(Synthetic | Implicit), name.toTermName, - tparams, vparamss, classTypeRef, creatorExpr) :: Nil + constrTparams, constrVparamss, classTypeRef, creatorExpr) :: Nil } else Nil - val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt - val self1 = + val self1 = { + val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt if (self.isEmpty) self else cpy.ValDef(self, self.mods | SelfName, self.name, selfType, self.rhs) + } + + val cdef1 = { + val originalTparams = constr1.tparams.toIterator + val originalVparams = constr1.vparamss.toIterator.flatten + val tparamAccessors = derivedTparams map { tdef => + cpy.TypeDef(tdef, originalTparams.next.mods, tdef.name, tdef.rhs, tdef.tparams) + } + val vparamAccessors = derivedVparamss.flatten map { vdef => + cpy.ValDef(vdef, originalVparams.next.mods, vdef.name, vdef.tpt, vdef.rhs) + } + cpy.TypeDef(cdef, mods, name, + cpy.Template(impl, constr, parents1, self1, + tparamAccessors ::: vparamAccessors ::: body ::: caseClassMeths)) + } + + // install the watch on classTycon + classTycon match { + case tycon: DerivedTypeTree => tycon.watching(cdef1) + case _ => + } - val cdef1 = cpy.TypeDef(cdef, mods, name, - cpy.Template(impl, constr, parents1, self1, - constr1.tparams ::: constr1.vparamss.flatten ::: body ::: caseClassMeths)) flatTree(cdef1 :: companions ::: implicitWrappers) } diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index b6ae6661f15b..66c436f9df2a 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -6,12 +6,13 @@ import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ import Denotations._, SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ import Decorators._ +import util.Attachment import language.higherKinds import collection.mutable.ListBuffer object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] { -// ----- Tree cases that exist in untyped form only ------------------ + // ----- Tree cases that exist in untyped form only ------------------ trait OpTree extends Tree { def op: Name @@ -61,6 +62,47 @@ object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] { override def withName(name: Name)(implicit ctx: Context) = cpy.PolyTypeDef(this, mods, name.toTypeName, tparams, rhs) } + // ----- TypeTrees that refer to other tree's symbols ------------------- + + /** A type tree that gets its type from some other tree's symbol. Enters the + * type tree in the References attachment of the `from` tree as a side effect. + */ + abstract class DerivedTypeTree extends TypeTree(EmptyTree) { + + private var myWatched: Tree = EmptyTree + + /** The watched tree; used only for printing */ + def watched: Tree = myWatched + + /** Install the derived type tree as a dependency on `original` */ + def watching(original: DefTree): this.type = { + myWatched = original + val existing = original.attachmentOrElse(References, Nil) + original.putAttachment(References, this :: existing) + this + } + + /** A hook to ensure that all necessary symbols are completed so that + * OriginalSymbol attachments are propagated to this tree + */ + def ensureCompletions(implicit ctx: Context): Unit = () + + /** The method that computes the type of this tree */ + def derivedType(originalSym: Symbol)(implicit ctx: Context): Type + } + + /** Attachment key containing TypeTrees whose type is computed + * from the symbol in this type. These type trees have marker trees + * TypeRefOfSym or InfoOfSym as their originals. + */ + val References = new Attachment.Key[List[Tree]] + + /** Attachment key for TypeTrees marked with TypeRefOfSym or InfoOfSym + * which contains the symbol of the original tree from which this + * TypeTree is derived. + */ + val OriginalSymbol = new Attachment.Key[Symbol] + // ------ Creation methods for untyped only ----------------- def Ident(name: Name): Ident = new Ident(name) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index bea8576c67d1..e05e80a3ba36 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -616,6 +616,7 @@ object SymDenotations { owner.info.decl(effectiveName.toTypeName) .suchThat(sym => sym.isClass && sym.isCoDefinedWith(symbol)) .symbol + //else ctx.defContext(symbol).denotNamed(effectiveName.toTypeName) ... todo: make completion else NoSymbol /** If this is a class, the module class of its companion object. diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5231ccd930b6..374c4491e22b 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -241,6 +241,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } case SeqLiteral(elems) => "[" ~ toTextGlobal(elems, ",") ~ "]" + case tpt: untpd.DerivedTypeTree => + "" case TypeTree(orig) => if (tree.hasType) toText(tree.typeOpt) else toText(orig) case SingletonTypeTree(ref) => diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index ea396519ad2a..36822cb851b5 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -84,6 +84,7 @@ trait Checking extends NoChecking { /** Check that (return) type of implicit definition is not empty */ override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { + case tpt: untpd.DerivedTypeTree => case TypeTree(untpd.EmptyTree) => val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index dadb0f9f8832..1df9774f4de5 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -72,6 +72,13 @@ trait NamerContextOps { this: Context => } go(this) } + + /** Context where `sym` is defined, assuming we are in a nested context. */ + def defContext(sym: Symbol) = + outersIterator + .dropWhile(_.owner != sym) + .dropWhile(_.owner == sym) + .next } /** This class creates symbols from definitions and imports and gives them @@ -173,10 +180,10 @@ class Namer { typer: Typer => enclosingClassNamed(mods.privateWithin, mods.pos) def record(sym: Symbol): Symbol = { - val refs = tree.attachmentOrElse(desugar.References, Nil) + val refs = tree.attachmentOrElse(References, Nil) if (refs.nonEmpty) { - tree.removeAttachment(desugar.References) - refs foreach (_.pushAttachment(desugar.OriginalSymbol, sym)) + tree.removeAttachment(References) + refs foreach (_.pushAttachment(OriginalSymbol, sym)) } tree.pushAttachment(SymOfTree, sym) sym @@ -449,17 +456,25 @@ class Namer { typer: Typer => if (self.isEmpty) NoType else if (cls is Module) cls.owner.thisType select sourceModule else createSymbol(self) + // pre-set info, so that parent types can refer to type params denot.info = ClassInfo(cls.owner.thisType, cls, Nil, decls, selfInfo) + + // Ensure constructor is completed so that any parameter accessors + // which have type trees deriving from its parameters can be + // completed in turn. Note that parent types access such parameter + // accessors, that's why the constructor needs to be completed before + // the parent types are elaborated. + index(constr) + symbolOfTree(constr).ensureCompleted() + val parentTypes = ensureFirstIsClass(parents map checkedParentType) val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls) typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs") - index(constr) index(rest)(inClassContext(selfInfo)) denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo) // make sure constr parameters are all entered because we refer to them in desugarings: - symbolOfTree(constr).ensureCompleted() } } @@ -509,13 +524,6 @@ class Namer { typer: Typer => if (!mdef.tpt.isEmpty) WildcardType else { - /** Context where `sym` is defined */ - def defContext(sym: Symbol) = - ctx.outersIterator - .dropWhile(_.owner != sym) - .dropWhile(_.owner == sym) - .next - /** An type for this definition that might be inherited from elsewhere: * If this is a setter parameter, the corresponding getter type. * If this is a class member, the conjunction of all result types @@ -561,7 +569,7 @@ class Namer { typer: Typer => if (original.isConstructorName && (sym.owner is ModuleClass)) sym.owner.companionClass.info.decl(nme.CONSTRUCTOR) else - defContext(sym).denotNamed(original) + ctx.defContext(sym).denotNamed(original) def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match { case params :: paramss1 => if (idx < params.length) wildApprox(params(idx)) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index f7e47e4bf793..f557f1f605f7 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -637,12 +637,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { if (tree.original.isEmpty) tree match { - case tree: desugar.DerivedTypeTree => - TypeTree(tree.derivedType(tree.attachment(desugar.OriginalSymbol))) withPos tree.pos - // btw, no need to remove the attachment. The typed - // tree is different from the untyped one, so the - // untyped tree is no longer accessed after all - // accesses with typedTypeTree are done. + case tree: untpd.DerivedTypeTree => + tree.ensureCompletions + try + TypeTree(tree.derivedType(tree.attachment(untpd.OriginalSymbol))) withPos tree.pos + // btw, no need to remove the attachment. The typed + // tree is different from the untyped one, so the + // untyped tree is no longer accessed after all + // accesses with typedTypeTree are done. + catch { + case ex: NoSuchElementException => + println(s"missing OriginalSymbol for ${ctx.owner.ownersIterator.toList}") + throw ex + } case _ => assert(isFullyDefined(pt, ForceDegree.none)) tree.withType(pt) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 546b09b3aa58..b1149d63a17a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -18,6 +18,7 @@ class tests extends CompilerTest { val posDir = "./tests/pos/" val negDir = "./tests/neg/" + val newDir = "./tests/new/" val dotcDir = "./src/dotty/" /* @Test def pos_Coder() = compileFile(posDir, "Coder", twice) @@ -45,6 +46,7 @@ class tests extends CompilerTest { @Test def pos_overloadedAccess = compileFile(posDir, "overloadedAccess", twice) */ @Test def pos_all = compileFiles(posDir, twice) + @Test def pos_new = compileFiles(newDir, "-Xprompt" :: Nil) @Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1) @Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4) diff --git a/tests/pos/A.scala b/tests/pos/A.scala new file mode 100644 index 000000000000..fc50379d88f6 --- /dev/null +++ b/tests/pos/A.scala @@ -0,0 +1,8 @@ +trait A extends java.lang.Object {} + +object test { + + def x: A = x; + +} + diff --git a/tests/pos/hygiene.scala b/tests/pos/hygiene.scala new file mode 100644 index 000000000000..25f2349599aa --- /dev/null +++ b/tests/pos/hygiene.scala @@ -0,0 +1,28 @@ +// Illustrates a failure with hygiene. + +object hygiene { + + class D[T] + + case class C[T](x: D[T]) +// gives +// 7: error: wrong number of type arguments for hygiene.C.D, should be 0 +// 7: error: constructor C in class C does not take type parameters +// +// The second error message is straightforward to fix using a refTypeTree for C in +// desugar.classDef.classTypeRef, but the first one is much harder. + + + object C { + + class C + +// class D + + } + + val c = C.apply(new D) + + c.x + +} diff --git a/tests/pos/t0017.scala b/tests/pos/t0017.scala new file mode 100644 index 000000000000..d2bcda08d486 --- /dev/null +++ b/tests/pos/t0017.scala @@ -0,0 +1,21 @@ +class Quantity { + def getValue = 0; + def connect(c: Constraint) = c.newValue; +} + +abstract class Constraint(q: Quantity) { + def newValue: Unit; + q connect this +} + +class Adder(q: Quantity) extends Constraint(q) { + def newValue = Console.println(q.getValue); +} + +object Main { + def main(args: Array[String]): Unit = { + val x = new Quantity; + new Adder(x); + () + } +} diff --git a/tests/pos/t0020.scala b/tests/pos/t0020.scala new file mode 100644 index 000000000000..4f1e5b1732a9 --- /dev/null +++ b/tests/pos/t0020.scala @@ -0,0 +1,8 @@ +object Exceptions { + + class CubeException(s: String) extends RuntimeException(s); + + def main(args: Array[String]) = + Console.println(new CubeException("test")); + +} diff --git a/tests/pos/t0029.scala b/tests/pos/t0029.scala new file mode 100644 index 000000000000..937b6d70c049 --- /dev/null +++ b/tests/pos/t0029.scala @@ -0,0 +1,3 @@ +object Main { + def f[a]: List[List[a]] = for (l1 <- Nil; l2 <- Nil) yield l1 +} diff --git a/tests/pos/t0030.scala b/tests/pos/t0030.scala new file mode 100644 index 000000000000..2f23be14d98a --- /dev/null +++ b/tests/pos/t0030.scala @@ -0,0 +1,9 @@ +trait A { + def f(x: Int): Unit; + def f(x: String): Unit; +} + +class B extends A { + def f(x: Int): Unit = (); + def f(x: String): Unit = (); +} diff --git a/tests/pos/t0031.scala b/tests/pos/t0031.scala new file mode 100644 index 000000000000..d4050c818441 --- /dev/null +++ b/tests/pos/t0031.scala @@ -0,0 +1,29 @@ +object Main { + + trait Ensure[a] { + def ensure(postcondition: a => Boolean): a + } + + def require[a](precondition: => Boolean)(command: => a): Ensure[a] = + if (precondition) + new Ensure[a] { + def ensure(postcondition: a => Boolean): a = { + val result = command; + if (postcondition(result)) result + else sys.error("Assertion error") + } + } + else + sys.error("Assertion error"); + + def arb[a](s: List[a]) = + require (! s.isEmpty) { + s.head + } ensure (result => s contains result); + + def main(args: Array[String]) = { + val s = List(1, 2); + Console.println(arb(s)) + } + +} diff --git a/tests/pos/t0032.scala b/tests/pos/t0032.scala new file mode 100644 index 000000000000..727a7d4e992d --- /dev/null +++ b/tests/pos/t0032.scala @@ -0,0 +1,17 @@ +import java.io.{OutputStream, PrintStream}; + +class PromptStream(s: OutputStream) extends PrintStream(s) { + override def println() = super.println(); +} + +object Main { + + val out = new PromptStream(java.lang.System.out); + + java.lang.System.setOut(out); + + def main(args: Array[String]) = + //out.println("hello world"); + () + +} diff --git a/tests/pos/t0036.scala b/tests/pos/t0036.scala new file mode 100644 index 000000000000..3c9a84f8ad33 --- /dev/null +++ b/tests/pos/t0036.scala @@ -0,0 +1,8 @@ +object m { + + val xs: List[Int] = Nil + def f(i: Int) = 0 + val v = xs map f + + def m() = {} +} diff --git a/tests/new/t0039.scala b/tests/pos/t0039.scala similarity index 100% rename from tests/new/t0039.scala rename to tests/pos/t0039.scala diff --git a/tests/pos/t0049.scala b/tests/pos/t0049.scala new file mode 100644 index 000000000000..dd866422637f --- /dev/null +++ b/tests/pos/t0049.scala @@ -0,0 +1,3 @@ +class C1(x: AnyRef) {}; + +class C2 extends C1({ class A extends AnyRef {}; (new A) : AnyRef }) {}; diff --git a/tests/pos/t0053.scala b/tests/pos/t0053.scala new file mode 100644 index 000000000000..44763ef144f3 --- /dev/null +++ b/tests/pos/t0053.scala @@ -0,0 +1,7 @@ +object bug { + def foobar[c]: Int = { + class Foo { def foo: Bar = new Bar(); } + class Bar { def bar: c = bar; } + 0 + } +} diff --git a/tests/pos/t0055.scala b/tests/pos/t0055.scala new file mode 100644 index 000000000000..0796294403a3 --- /dev/null +++ b/tests/pos/t0055.scala @@ -0,0 +1,6 @@ +class X(x : Any) +class W { + new X(new Z() with Y) {} + trait Y { def y = () } +} +class Z(r : Any) { def this() = this(null) } diff --git a/tests/pos/t0061.scala b/tests/pos/t0061.scala new file mode 100644 index 000000000000..dd3f94f30cec --- /dev/null +++ b/tests/pos/t0061.scala @@ -0,0 +1,10 @@ +object O { + + class testClass ; + + case class testA() extends testClass ; // works if you leave away "extends..." + // or if you write TestA + def ga( x:testClass ) = x match { + case testA() => () + } +} diff --git a/tests/pos/t0064.scala b/tests/pos/t0064.scala new file mode 100644 index 000000000000..1eeca8dcad44 --- /dev/null +++ b/tests/pos/t0064.scala @@ -0,0 +1,6 @@ +object B { + def main(Args:Array[String]) = { + val (_,x) = (1,2); + x + 1; + } +} diff --git a/tests/pos/t0066.scala b/tests/pos/t0066.scala new file mode 100644 index 000000000000..2153264e7a29 --- /dev/null +++ b/tests/pos/t0066.scala @@ -0,0 +1,7 @@ +class GBTree[A, B] /*with Map[A, B, GBTree[A,B]]*/ { + abstract class Tree[A,B]; + case class Node[A,B](key:A,value:B,smaller:Node[A,B],bigger:Node[A,B]) + extends Tree[A,B]; + case class Nil[A,B]() extends Tree[A,B]; + +} diff --git a/tests/pos/t0068.scala b/tests/pos/t0068.scala new file mode 100644 index 000000000000..beb2c7c0abbd --- /dev/null +++ b/tests/pos/t0068.scala @@ -0,0 +1,6 @@ +class E { + def f() = { + val (_::l1) = List(1,2,3); + l1.tail; + } +} diff --git a/tests/pos/t0069.scala b/tests/pos/t0069.scala new file mode 100644 index 000000000000..e4c242c0ee6c --- /dev/null +++ b/tests/pos/t0069.scala @@ -0,0 +1,10 @@ +object testCQ { + // why does this not work directly + case class Thing( name:String, contains:List[ Thing ] ); + + /* ... but this one does? + abstract class T; + case class Thing2( name:String, contains:List[ T ] ) extends T; + */ + +} diff --git a/tests/pos/t0076.scala b/tests/pos/t0076.scala new file mode 100644 index 000000000000..5419cf515436 --- /dev/null +++ b/tests/pos/t0076.scala @@ -0,0 +1,9 @@ +// This is extracted from a test file => don't add a new test file. +object bug { + def foo(i: => Int): Int = 0; + + def bar: Int = { + var i: Int = 0; + foo (i); + } +} diff --git a/tests/pos/t0081.scala b/tests/pos/t0081.scala new file mode 100644 index 000000000000..20fd60497437 --- /dev/null +++ b/tests/pos/t0081.scala @@ -0,0 +1,4 @@ +class A { + val b: A#B = new B; + class B {} +} diff --git a/tests/pos/t0082.scala b/tests/pos/t0082.scala new file mode 100644 index 000000000000..2b365ca3333b --- /dev/null +++ b/tests/pos/t0082.scala @@ -0,0 +1,18 @@ + +object Main { + + def min0[A](less: (A, A) => Boolean, xs: List[A]): Option[A] = xs match { + case List() => None + case List(x) => Some(x) +// case x :: Nil => Some(x) + case y :: ys => (min0(less, ys): @unchecked) match { + case Some(m) => if (less(y, m)) Some(y) else Some(m) + } + } + + def min(xs: List[Int]) = min0((x: Int, y: Int) => x < y, xs); + + def main(args: Array[String]) = + Console.println(min(List())); + +} diff --git a/tests/pos/t0085.scala b/tests/pos/t0085.scala new file mode 100644 index 000000000000..e018afb6ee02 --- /dev/null +++ b/tests/pos/t0085.scala @@ -0,0 +1,8 @@ +object A { + case class B(c: C) { + class C; + } + class C; + val b: B = new B(new C()); + val c: C = b.c; +} diff --git a/tests/pos/t0091.scala b/tests/pos/t0091.scala new file mode 100644 index 000000000000..d491b7cfb93d --- /dev/null +++ b/tests/pos/t0091.scala @@ -0,0 +1,6 @@ +class Bug { + def main(args: Array[String]) = { + var msg: String = null; // no bug if "null" instead of "_" + val f: PartialFunction[Any, Unit] = { case 42 => msg = "coucou" }; + } +} diff --git a/tests/pos/t0093.scala b/tests/pos/t0093.scala new file mode 100644 index 000000000000..d648d773b08c --- /dev/null +++ b/tests/pos/t0093.scala @@ -0,0 +1,4 @@ +object Bug { + def f(cond: => Boolean) = while (cond == false) {}; + // no bug with "false == cond" +} diff --git a/tests/pos/t0095.scala b/tests/pos/t0095.scala new file mode 100644 index 000000000000..2123237b5412 --- /dev/null +++ b/tests/pos/t0095.scala @@ -0,0 +1,15 @@ +class ParseResult[+T] +case class Success[+T](t: T) extends ParseResult[T] + +abstract class Nonterminal[Output] { + + type SubNonterminal = Nonterminal[_ <: Output] + + def parse: ParseResult[Output] + + def parse1(nts: List[SubNonterminal]): ParseResult[Output] = + nts match { + case nt::nts => nt.parse match { case Success(so) => Success(so) } + case Nil => throw new Error + } +}