aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorodersky <odersky@gmail.com>2014-03-21 09:17:59 +0100
committerodersky <odersky@gmail.com>2014-03-21 09:17:59 +0100
commitfd76c38000f206b3d27ac68eaeddb0f76678dfc2 (patch)
tree8355f0ef64102f569ebd970e49c2a591116d0b44
parentb5864b48d04adf6cab1dbe58d394ad608dafd440 (diff)
parente50646c21cbc842c1188fc876e16ea2b3e2a2ea3 (diff)
downloaddotty-fd76c38000f206b3d27ac68eaeddb0f76678dfc2.tar.gz
dotty-fd76c38000f206b3d27ac68eaeddb0f76678dfc2.tar.bz2
dotty-fd76c38000f206b3d27ac68eaeddb0f76678dfc2.zip
Merge pull request #88 from dotty-staging/try/hygienic-desugaring
Try/hygienic desugaring
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala158
-rw-r--r--src/dotty/tools/dotc/ast/untpd.scala44
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala3
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala40
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala28
-rw-r--r--test/dotc/tests.scala2
-rw-r--r--test/test/ShowClassTests.scala2
-rw-r--r--tests/pos/A.scala8
-rw-r--r--tests/pos/hygiene.scala20
-rw-r--r--tests/pos/t0017.scala21
-rw-r--r--tests/pos/t0020.scala8
-rw-r--r--tests/pos/t0029.scala3
-rw-r--r--tests/pos/t0030.scala9
-rw-r--r--tests/pos/t0031.scala29
-rw-r--r--tests/pos/t0032.scala17
-rw-r--r--tests/pos/t0036.scala8
-rw-r--r--tests/pos/t0039.scala (renamed from tests/new/t0039.scala)0
-rw-r--r--tests/pos/t0049.scala3
-rw-r--r--tests/pos/t0053.scala7
-rw-r--r--tests/pos/t0055.scala6
-rw-r--r--tests/pos/t0061.scala10
-rw-r--r--tests/pos/t0064.scala6
-rw-r--r--tests/pos/t0066.scala7
-rw-r--r--tests/pos/t0068.scala6
-rw-r--r--tests/pos/t0069.scala5
-rw-r--r--tests/pos/t0076.scala8
-rw-r--r--tests/pos/t0081.scala4
-rw-r--r--tests/pos/t0082.scala17
-rw-r--r--tests/pos/t0085.scala8
-rw-r--r--tests/pos/t0091.scala6
-rw-r--r--tests/pos/t0093.scala4
-rw-r--r--tests/pos/t0095.scala15
33 files changed, 458 insertions, 56 deletions
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 0c741a652..ea9e7566a 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -8,6 +8,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
import Decorators._
import language.higherKinds
import collection.mutable.ListBuffer
+import config.Printers._
import typer.ErrorReporting.InfoString
import typer.Mode
@@ -21,6 +22,67 @@ object desugar {
/** Info of a variable in a pattern: The named tree and its type */
private type VarInfo = (NameTree, Tree)
+// ----- DerivedTypeTrees -----------------------------------
+
+ class SetterParamTree extends DerivedTypeTree {
+ def derivedType(sym: Symbol)(implicit ctx: Context) = sym.info.resultType
+ }
+
+ class TypeRefTree extends DerivedTypeTree {
+ def derivedType(sym: Symbol)(implicit ctx: Context) = sym.typeRef
+ }
+
+ class DerivedFromParamTree extends DerivedTypeTree {
+
+ /** 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)
+ }
+ }
+
+ /** 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 -------------------------------------------------
+
/** var x: Int = expr
* ==>
* def x: Int = expr
@@ -35,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 = TypeTree())
+ 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,
@@ -151,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(
@@ -166,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
@@ -201,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
}
@@ -226,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 }
@@ -256,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)
}
@@ -283,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 =>
+ "<derived typetree watching " ~ toText(tpt.watched) ~ ">"
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 39da1dbae..36822cb85 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -84,7 +84,8 @@ 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 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)
case _ =>
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index c318ddf36..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,6 +180,11 @@ class Namer { typer: Typer =>
enclosingClassNamed(mods.privateWithin, mods.pos)
def record(sym: Symbol): Symbol = {
+ val refs = tree.attachmentOrElse(References, Nil)
+ if (refs.nonEmpty) {
+ tree.removeAttachment(References)
+ refs foreach (_.pushAttachment(OriginalSymbol, sym))
+ }
tree.pushAttachment(SymOfTree, sym)
sym
}
@@ -444,15 +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:
}
}
@@ -502,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
@@ -516,10 +531,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)
@@ -557,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))
@@ -578,7 +590,7 @@ class Namer { typer: Typer =>
val rhsCtx = ctx.fresh addMode Mode.InferringReturnType
def rhsType = typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen.approximateUnion
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 f961a4544..67e5c5902 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -635,11 +635,29 @@ 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)
+ tree match {
+ 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)
+ }
+ else {
+ val original1 = typed(tree.original)
+ cpy.TypeTree(tree, original1).withType(original1.tpe)
+ }
}
def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") {
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/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) = {
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..9bbf73c0f
--- /dev/null
+++ b/tests/pos/hygiene.scala
@@ -0,0 +1,20 @@
+// Illustrates a use case where we need hygiene.
+
+object hygiene {
+
+ class D[T]
+
+ case class C[T](x: D[T])
+// without hygiene, this gave
+// 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
+
+ object C {
+ class C
+ }
+
+ 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/new/t0039.scala b/tests/pos/t0039.scala
index 652c606b0..652c606b0 100644
--- a/tests/new/t0039.scala
+++ b/tests/pos/t0039.scala
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..8a3aed7c6
--- /dev/null
+++ b/tests/pos/t0061.scala
@@ -0,0 +1,10 @@
+object O {
+
+ class testClass ;
+
+ case class testA() extends testClass ;
+
+ 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..8ac328908
--- /dev/null
+++ b/tests/pos/t0066.scala
@@ -0,0 +1,7 @@
+class 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..23a25dbec
--- /dev/null
+++ b/tests/pos/t0069.scala
@@ -0,0 +1,5 @@
+object testCQ {
+
+ case class Thing( name:String, contains:List[ Thing ] );
+
+}
diff --git a/tests/pos/t0076.scala b/tests/pos/t0076.scala
new file mode 100644
index 000000000..bac26f2ab
--- /dev/null
+++ b/tests/pos/t0076.scala
@@ -0,0 +1,8 @@
+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..2a9e549b1
--- /dev/null
+++ b/tests/pos/t0082.scala
@@ -0,0 +1,17 @@
+
+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 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..414e2c931
--- /dev/null
+++ b/tests/pos/t0091.scala
@@ -0,0 +1,6 @@
+class Bug {
+ def main(args: Array[String]) = {
+ var msg: String = null;
+ 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
+ }
+}