From 06690117f3b67d947df5ef14cd02a55341b94d1c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Jun 2015 10:31:48 +0200 Subject: [PATCH 1/2] Fix stack overflow when testing for shadowing Shadowing tests could go into an infinite recursion when the found sahdwoing member itself needs an implicit that is resolved and then shadowed again by the same member. A test case is neg/arrayclone-new.scala. This caused a SO before, now gives two errors. --- src/dotty/tools/dotc/typer/Implicits.scala | 5 +++-- src/dotty/tools/dotc/typer/Mode.scala | 5 +++++ test/dotc/tests.scala | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index b03bcfcf96db..841390ef1b22 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -491,7 +491,7 @@ trait Implicits { self: Typer => pt) val generated1 = adapt(generated, pt) lazy val shadowing = - typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(nestedContext.setNewTyperState) + typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(nestedContext.setNewTyperState.addMode(Mode.ImplicitShadowing)) def refMatches(shadowing: Tree): Boolean = ref.symbol == closureBody(shadowing).symbol || { shadowing match { @@ -501,7 +501,8 @@ trait Implicits { self: Typer => } if (ctx.typerState.reporter.hasErrors) nonMatchingImplicit(ref) - else if (contextual && !shadowing.tpe.isError && !refMatches(shadowing)) { + else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) && + !shadowing.tpe.isError && !refMatches(shadowing)) { implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}") shadowedImplicit(ref, methPart(shadowing).tpe) } diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala index 8889cf604ca4..e84ef27846e3 100644 --- a/src/dotty/tools/dotc/typer/Mode.scala +++ b/src/dotty/tools/dotc/typer/Mode.scala @@ -68,5 +68,10 @@ object Mode { */ val Printing = newMode(10, "Printing") + /** We are currently typechecking an ident to determine whether some implicit + * is shadowed - don't do any other shadowing tests. + */ + val ImplicitShadowing = newMode(11, "ImplicitShadowing") + val PatternOrType = Pattern | Type } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index a7123602badf..0a6127580df9 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -137,6 +137,7 @@ class tests extends CompilerTest { @Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2) @Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8) @Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5) + @Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2) @Test def run_all = runFiles(runDir) From 084d4bde8ab869c5f98c2720f03db112ea13531b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Jun 2015 15:16:30 +0200 Subject: [PATCH 2/2] Add missing test --- tests/neg/arrayclone-new.scala | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/neg/arrayclone-new.scala diff --git a/tests/neg/arrayclone-new.scala b/tests/neg/arrayclone-new.scala new file mode 100644 index 000000000000..4e33a7d8cde6 --- /dev/null +++ b/tests/neg/arrayclone-new.scala @@ -0,0 +1,38 @@ +// Run with -explaintypes to see information about shadowing failures +import scala.reflect.{ClassTag, classTag} + +object Test extends dotty.runtime.LegacyApp{ + ObjectArrayClone; + PolymorphicArrayClone; +} + +object ObjectArrayClone{ + val it : Array[String] = Array("1", "0"); + val cloned = it.clone(); + assert(cloned.sameElements(it)); + cloned(0) = "0"; + assert(it(0) == "1") +} + +object PolymorphicArrayClone{ + def testIt[T](it : Array[T], one : T, zero : T) = { + val cloned = it.clone(); + assert(cloned.sameElements(it)); + cloned(0) = zero; + assert(it(0) == one) + } + + testIt(Array("one", "two"), "one", "two"); + + class Mangler[T: ClassTag](ts : T*){ + // this will always be a BoxedAnyArray even after we've unboxed its contents. + val it = ts.toArray[T]; + } + + val mangled = new Mangler[Int](0, 1); + + val y : Array[Int] = mangled.it; // make sure it's unboxed + + testIt(mangled.it, 0, 1); +} +