From ce466da9fd5b2601c91e351fd1d288099ec380b2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Mar 2014 15:53:42 +0100 Subject: Better diagnostics for failing stub count tests --- test/test/ShowClassTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/test/ShowClassTests.scala b/test/test/ShowClassTests.scala index f683b26e7..46f9e385e 100644 --- a/test/test/ShowClassTests.scala +++ b/test/test/ShowClassTests.scala @@ -66,7 +66,7 @@ class ShowClassTests extends DottyTest { showPackage(ctx.requiredPackage(path)) val nstubs = Symbols.stubs.length debug_println(s"$nstubs stubs") - assert(nstubs <= expectedStubs, s"stubs found $nstubs, expected: $expectedStubs") + assert(nstubs <= expectedStubs, s"stubs found: $nstubs, expected: $expectedStubs\nstubs: ${Symbols.stubs.mkString(",")}") } def showClass(cls: Symbol)(implicit ctx: Context) = { -- cgit v1.2.3 From a4516eac1be98830c99ea48e9baedf022dfcb9f7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 12 Mar 2014 15:06:17 +0100 Subject: 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 +++++- src/dotty/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/new/t0039.scala | 6 - 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/pos/t0039.scala | 6 + 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, 453 insertions(+), 83 deletions(-) delete mode 100644 tests/new/t0039.scala 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 create mode 100644 tests/pos/t0039.scala 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 (limited to 'test') diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 4a635d259..ea9e7566a 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 SetterParamTree 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 SetterParamTree).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,31 +231,35 @@ 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. val classTypeRef/*: Tree*/ = { // -language:keepUnions difference: classTypeRef needs type annotation, otherwise // 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 @@ -233,23 +270,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 } @@ -258,15 +295,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 } @@ -288,17 +324,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) } @@ -315,19 +352,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 4e55fe868..44f340932 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 UntypedTreeInfo { -// ----- 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 UntypedTreeInfo { 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/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 0d84a9e61..1a65b2978 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 ea396519a..36822cb85 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 162e13bbc..7885e85ac 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 e9a79eeb1..67e5c5902 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 0caef3cae..1ddd0a578 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) @@ -46,6 +47,7 @@ class tests extends CompilerTest { @Test def pos_approximateUnion = compileFile(posDir, "approximateUnion", 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/new/t0039.scala b/tests/new/t0039.scala deleted file mode 100644 index 652c606b0..000000000 --- a/tests/new/t0039.scala +++ /dev/null @@ -1,6 +0,0 @@ -abstract class Extensible[A, This <: Extensible[A, This]](x: A, xs: This) { self: This => - def mkObj(x: A, xs: This): This; -} -class Fixed[A](x: A, xs: Fixed[A]) extends Extensible[A, Fixed[A]](x, xs) { - def mkObj(x: A, xs: Fixed[A]) = new Fixed(x, xs); -} diff --git a/tests/pos/A.scala b/tests/pos/A.scala new file mode 100644 index 000000000..fc50379d8 --- /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 000000000..25f234959 --- /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 000000000..d2bcda08d --- /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 000000000..4f1e5b173 --- /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 000000000..937b6d70c --- /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 000000000..2f23be14d --- /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 000000000..d4050c818 --- /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 000000000..727a7d4e9 --- /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 000000000..3c9a84f8a --- /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/pos/t0039.scala b/tests/pos/t0039.scala new file mode 100644 index 000000000..652c606b0 --- /dev/null +++ b/tests/pos/t0039.scala @@ -0,0 +1,6 @@ +abstract class Extensible[A, This <: Extensible[A, This]](x: A, xs: This) { self: This => + def mkObj(x: A, xs: This): This; +} +class Fixed[A](x: A, xs: Fixed[A]) extends Extensible[A, Fixed[A]](x, xs) { + def mkObj(x: A, xs: Fixed[A]) = new Fixed(x, xs); +} diff --git a/tests/pos/t0049.scala b/tests/pos/t0049.scala new file mode 100644 index 000000000..dd8664226 --- /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 000000000..44763ef14 --- /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 000000000..079629440 --- /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 000000000..dd3f94f30 --- /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 000000000..1eeca8dca --- /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 000000000..2153264e7 --- /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 000000000..beb2c7c0a --- /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 000000000..e4c242c0e --- /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 000000000..5419cf515 --- /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 000000000..20fd60497 --- /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 000000000..2b365ca33 --- /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 000000000..e018afb6e --- /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 000000000..d491b7cfb --- /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 000000000..d648d773b --- /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 000000000..2123237b5 --- /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 + } +} -- cgit v1.2.3