From ba8d9ea2e4b887fc0faa1f636aade84c45292144 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Mar 2014 11:06:49 +0100 Subject: [PATCH 1/3] Fix for t1002 Need to compile the self type of a class not in the context of the class members but one context further out. Reason: self type should not be able to see the members. --- src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/pos/t0002.scala | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/pos/t0002.scala diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 51eba3b02564..6a1d279df8b3 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -786,7 +786,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val constr1 = typed(constr).asInstanceOf[DefDef] val parents1 = ensureConstrCall(ensureFirstIsClass( parents mapconserve typedParent, cdef.pos.toSynthetic)) - val self1 = typed(self).asInstanceOf[ValDef] + val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class memebers are not visible val localDummy = ctx.newLocalDummy(cls, impl.pos) val body1 = typedStats(body, localDummy)(inClassContext(self1.symbol)) checkNoDoubleDefs(cls) diff --git a/tests/pos/t0002.scala b/tests/pos/t0002.scala new file mode 100644 index 000000000000..4c58ed3f4f6d --- /dev/null +++ b/tests/pos/t0002.scala @@ -0,0 +1,6 @@ +object main { + def main(args: Array[String]) = { + val b = true; + while (b == true) { } + } +} From 7fa78597bf58a7759303095121a432cb258f447c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Mar 2014 17:48:55 +0100 Subject: [PATCH 2/3] Fix problems related to t0039 This test case exercised several problems: 1.)2.) Two ways to run into a cyclic references. Fixed by - assuming an early info when completing a typedef, similarly to what is done for a classdef - doing wellformed bounds checking in a later phase. Failure to check whether arguments correspond to F-bounds. - a substitution was missing. --- src/dotty/tools/dotc/core/Flags.scala | 2 +- src/dotty/tools/dotc/core/SymDenotations.scala | 11 ++++++++--- src/dotty/tools/dotc/typer/Checking.scala | 7 +++++-- src/dotty/tools/dotc/typer/Namer.scala | 1 + src/dotty/tools/dotc/typer/Typer.scala | 5 +++-- tests/new/t0039.scala | 6 ++++++ 6 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 tests/new/t0039.scala diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 0678b79beb06..620cd4fa5ebd 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -467,7 +467,7 @@ object Flags { final val ExpandedTypeParam = allOf(ExpandedName, TypeParam) /** A parameter or parameter accessor */ - final val ParamOrAccessor = Param | Accessor + final val ParamOrAccessor = Param | ParamAccessor /** A covariant type parameter instance */ final val LocalCovariant = allOf(Local, Covariant) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 5fbf6c2c423b..bbe5cf986c03 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -145,9 +145,14 @@ object SymDenotations { if (myFlags is Touched) throw new CyclicReference(this) myFlags |= Touched - completions.println(s"completing ${this.debugString}") - completer.complete(this) - completions.println(s"completed ${this.debugString}") + // completions.println(s"completing ${this.debugString}") + try completer.complete(this) + catch { + case ex: CyclicReference => + completions.println(s"error while completing ${this.debugString}") + throw ex + } + // completions.println(s"completed ${this.debugString}") } protected[dotc] final def info_=(tp: Type) = { diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 25e6a7aa7182..e3024b9a6e9d 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -45,13 +45,16 @@ trait Checking extends NoChecking { } /** Check that type arguments `args` conform to corresponding bounds in `poly` */ - override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = + override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = { + val argTypes = args.tpes + def substituted(tp: Type) = tp.substParams(poly, argTypes) for ((arg, bounds) <- args zip poly.paramBounds) { def notConforms(which: String, bound: Type) = ctx.error(i"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos) - if (!(arg.tpe <:< bounds.hi)) notConforms("upper", bounds.hi) + if (!(arg.tpe <:< substituted(bounds.hi))) notConforms("upper", bounds.hi) if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo) } + } /** Check that type `tp` is stable. * @return The type itself diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index c24021936d57..9c8522b8303c 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -614,6 +614,7 @@ class Namer { typer: Typer => def typeDefSig(tdef: TypeDef, sym: Symbol)(implicit ctx: Context): Type = { completeParams(tdef.tparams) + sym.info = TypeBounds.empty // avoid cyclic reference errors for F-bounds val tparamSyms = tdef.tparams map symbolOfTree val rhsType = typedAheadType(tdef.rhs).tpe diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 6a1d279df8b3..e195c2807db3 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -699,8 +699,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree) val lo1 = typed(lo) val hi1 = typed(hi) - if (!(lo1.tpe <:< hi1.tpe)) - ctx.error(i"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos) + if (false) // need to do in later phase, as this might cause a cyclic reference error. See pos/t0039.scala + if (!(lo1.tpe <:< hi1.tpe)) + ctx.error(i"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos) assignType(cpy.TypeBoundsTree(tree, lo1, hi1), lo1, hi1) } diff --git a/tests/new/t0039.scala b/tests/new/t0039.scala new file mode 100644 index 000000000000..652c606b0e38 --- /dev/null +++ b/tests/new/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); +} From 66291018e0512b2d4e7d6bac017ab47b95939275 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 12 Mar 2014 17:02:47 +0100 Subject: [PATCH 3/3] Fix constructor completion problem detected in t0054 Constructors need to be completed in the context which immediately encloses a class. Otherwise type references in the constructor see the wrong types, as is demonstrated in t0054. The difficulty here is that the inner class B nested in A also extends from A. Then it makes a difference whether the constructor parameter types of B are resolved in the context of B or in the context of A. Added explanation for context handling of constructors. --- src/dotty/tools/dotc/core/SymDenotations.scala | 6 +++--- src/dotty/tools/dotc/typer/Namer.scala | 11 ++++++++++- src/dotty/tools/dotc/typer/Typer.scala | 6 +++--- tests/pos/t0054.scala | 4 ++++ 4 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tests/pos/t0054.scala diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index bbe5cf986c03..b92556fa6021 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -148,9 +148,9 @@ object SymDenotations { // completions.println(s"completing ${this.debugString}") try completer.complete(this) catch { - case ex: CyclicReference => - completions.println(s"error while completing ${this.debugString}") - throw ex + case ex: CyclicReference => + completions.println(s"error while completing ${this.debugString}") + throw ex } // completions.println(s"completed ${this.debugString}") } diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 9c8522b8303c..2c5022726098 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -216,9 +216,18 @@ class Namer { typer: Typer => checkNoConflict(name) val deferred = if (lacksDefinition(tree)) Deferred else EmptyFlags val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags + + // to complete a constructor, move one context further out -- this + // is the context enclosing the class. Note that the context in which a + // constructor is recorded and the context in which it is completed are + // different: The former must have the class as owner (because the + // constructor is owned by the class), the latter must not (because + // constructor parameters are interpreted as if they are outside the class). + val cctx = if (tree.name == nme.CONSTRUCTOR) ctx.outer else ctx + record(ctx.newSymbol( ctx.owner, name, tree.mods.flags | deferred | method, - adjustIfModule(new Completer(tree), tree), + adjustIfModule(new Completer(tree)(cctx), tree), privateWithinClass(tree.mods), tree.pos)) case tree: Import => record(ctx.newSymbol( diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index e195c2807db3..ef8ce9eed381 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -699,9 +699,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree) val lo1 = typed(lo) val hi1 = typed(hi) - if (false) // need to do in later phase, as this might cause a cyclic reference error. See pos/t0039.scala - if (!(lo1.tpe <:< hi1.tpe)) - ctx.error(i"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos) + // need to do in later phase, as this might cause a cyclic reference error. See pos/t0039.scala + // if (!(lo1.tpe <:< hi1.tpe)) + // ctx.error(i"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos) assignType(cpy.TypeBoundsTree(tree, lo1, hi1), lo1, hi1) } diff --git a/tests/pos/t0054.scala b/tests/pos/t0054.scala new file mode 100644 index 000000000000..670160f36e48 --- /dev/null +++ b/tests/pos/t0054.scala @@ -0,0 +1,4 @@ +class A { + case class B(x: C) extends A { val z: A.this.C = x } + class C {} +}