From 053cd236ee12f877cfd71affb9a62a417f917d3d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Apr 2017 16:35:01 +0200 Subject: Fix #2152: Instantiate dependent result type parameters #2152 shows that dependent result type parameters can end up in the types of terms, so we have to eliminate them. If we don't we get orphan parameters in pickling. Fix method name and comment --- compiler/src/dotty/tools/dotc/core/NameKinds.scala | 2 +- compiler/src/dotty/tools/dotc/core/TypeOps.scala | 4 ++++ compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala | 17 +++++++++-------- tests/pos/i2152.scala | 7 +++++++ 5 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 tests/pos/i2152.scala diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 0f08e4701..81ac3a02f 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -209,7 +209,7 @@ object NameKinds { val InlineAccessorName = new UniqueNameKind("$_inlineAccessor_$") val TempResultName = new UniqueNameKind("ev$") val EvidenceParamName = new UniqueNameKind("evidence$") - val DepParamName = new UniqueNameKind("") + val DepParamName = new UniqueNameKind("(param)") val LazyImplicitName = new UniqueNameKind("$_lazy_implicit_$") val LazyLocalName = new UniqueNameKind("$lzy") val LazyLocalInitName = new UniqueNameKind("$lzyINIT") diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 9593bfe93..0de823e97 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -7,6 +7,7 @@ import SymDenotations._, Denotations.SingleDenotation import config.Printers.typr import util.Positions._ import NameOps._ +import NameKinds.DepParamName import Decorators._ import StdNames._ import Annotations._ @@ -169,6 +170,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object. simplify(l, theMap) & simplify(r, theMap) case OrType(l, r) => simplify(l, theMap) | simplify(r, theMap) + case tp: TypeVar if tp.origin.paramName.is(DepParamName) => + val effectiveVariance = if (theMap == null) 1 else theMap.variance + tp.instanceOpt orElse tp.instantiate(fromBelow = effectiveVariance != -1) case _ => (if (theMap != null) theMap else new SimplifyMap).mapOver(tp) } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c8c1886cc..955a5a11c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3509,7 +3509,7 @@ object Types { def apply(tp: Type): Type - protected var variance = 1 + protected[core] var variance = 1 protected def derivedSelect(tp: NamedType, pre: Type): Type = tp.derivedSelect(pre) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 398a7a17e..5d8240362 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -377,9 +377,10 @@ object ProtoTypes { * Also, if `owningTree` is non-empty, add a type variable for each parameter. * @return The added type lambda, and the list of created type variables. */ - def constrained(tl: TypeLambda, owningTree: untpd.Tree)(implicit ctx: Context): (TypeLambda, List[TypeTree]) = { + def constrained(tl: TypeLambda, owningTree: untpd.Tree, alwaysAddTypeVars: Boolean = false)(implicit ctx: Context): (TypeLambda, List[TypeTree]) = { val state = ctx.typerState - assert(!(ctx.typerState.isCommittable && owningTree.isEmpty), + val addTypeVars = alwaysAddTypeVars || !owningTree.isEmpty + assert(!(ctx.typerState.isCommittable && !addTypeVars), s"inconsistent: no typevars were added to committable constraint ${state.constraint}") def newTypeVars(tl: TypeLambda): List[TypeTree] = @@ -392,7 +393,7 @@ object ProtoTypes { val added = if (state.constraint contains tl) tl.newLikeThis(tl.paramNames, tl.paramInfos, tl.resultType) else tl - val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) + val tvars = if (addTypeVars) newTypeVars(added) else Nil ctx.typeComparer.addToConstraint(added, tvars.tpes.asInstanceOf[List[TypeVar]]) (added, tvars) } @@ -400,13 +401,13 @@ object ProtoTypes { /** Same as `constrained(tl, EmptyTree)`, but returns just the created type lambda */ def constrained(tl: TypeLambda)(implicit ctx: Context): TypeLambda = constrained(tl, EmptyTree)._1 - /** Create a new TypeParamRef that represents a dependent method parameter singleton */ - def newDepTypeParamRef(tp: Type)(implicit ctx: Context): TypeParamRef = { + /** Create a new TypeVar that represents a dependent method parameter singleton */ + def newDepTypeVar(tp: Type)(implicit ctx: Context): TypeVar = { val poly = PolyType(DepParamName.fresh().toTypeName :: Nil)( pt => TypeBounds.upper(AndType(tp, defn.SingletonType)) :: Nil, pt => defn.AnyType) - ctx.typeComparer.addToConstraint(poly, Nil) - TypeParamRef(poly, 0) + constrained(poly, untpd.EmptyTree, alwaysAddTypeVars = true) + ._2.head.tpe.asInstanceOf[TypeVar] } /** The result type of `mt`, where all references to parameters of `mt` are @@ -415,7 +416,7 @@ object ProtoTypes { def resultTypeApprox(mt: MethodType)(implicit ctx: Context): Type = if (mt.isDependent) { def replacement(tp: Type) = - if (ctx.mode.is(Mode.TypevarsMissContext)) WildcardType else newDepTypeParamRef(tp) + if (ctx.mode.is(Mode.TypevarsMissContext)) WildcardType else newDepTypeVar(tp) mt.resultType.substParams(mt, mt.paramInfos.map(replacement)) } else mt.resultType diff --git a/tests/pos/i2152.scala b/tests/pos/i2152.scala new file mode 100644 index 000000000..2171a487e --- /dev/null +++ b/tests/pos/i2152.scala @@ -0,0 +1,7 @@ +class Contra[-D](task: AnyRef) +object Test { + def narrow(task: AnyRef): Contra[task.type] = new Contra(task) + def ident[Before](elems: Contra[Before]): Contra[Before] = elems + val foo = null + ident(narrow(foo)) +} -- cgit v1.2.3 From aaff621f552e6824db4f7c1d73d9e8941e088d96 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 11 Apr 2017 23:36:16 +0200 Subject: Alternative fix The original fix made run/hmap-covariant fail because a type variable representing a dependent result parameter was instantiated. Trying something else now. --- compiler/src/dotty/tools/dotc/core/TypeOps.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 0de823e97..4a1c3d044 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -159,7 +159,11 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case tp1 => tp1 } case tp: TypeParamRef => - typerState.constraint.typeVarOfParam(tp) orElse tp + if (tp.paramName.is(DepParamName)) { + val bounds = ctx.typeComparer.bounds(tp) + if (bounds.lo.isRef(defn.NothingClass)) bounds.hi else bounds.lo + } + else typerState.constraint.typeVarOfParam(tp) orElse tp case _: ThisType | _: BoundType | NoPrefix => tp case tp: RefinedType => @@ -170,9 +174,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object. simplify(l, theMap) & simplify(r, theMap) case OrType(l, r) => simplify(l, theMap) | simplify(r, theMap) - case tp: TypeVar if tp.origin.paramName.is(DepParamName) => - val effectiveVariance = if (theMap == null) 1 else theMap.variance - tp.instanceOpt orElse tp.instantiate(fromBelow = effectiveVariance != -1) case _ => (if (theMap != null) theMap else new SimplifyMap).mapOver(tp) } -- cgit v1.2.3 From 25b5705d4183191b642f520e5ab3bc635f0ddde3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 11 Apr 2017 22:26:04 +0200 Subject: Fix #2230: Add regression test. --- .../test/dotty/tools/dotc/CompilationTests.scala | 1 + .../neg/customArgs/overloadsOnAbstractTypes.scala | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/neg/customArgs/overloadsOnAbstractTypes.scala diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 742b93fae..b3604d38a 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -151,6 +151,7 @@ class CompilationTests extends ParallelSummaryReport with ParallelTesting { compileFile("../tests/neg/customArgs/nopredef.scala", defaultOptions.and("-Yno-predef")) + compileFile("../tests/neg/customArgs/noimports.scala", defaultOptions.and("-Yno-imports")) + compileFile("../tests/neg/customArgs/noimports2.scala", defaultOptions.and("-Yno-imports")) + + compileFile("../tests/neg/customArgs/overloadsOnAbstractTypes.scala", allowDoubleBindings) + compileFile("../tests/neg/tailcall/t1672b.scala", defaultOptions) + compileFile("../tests/neg/tailcall/t3275.scala", defaultOptions) + compileFile("../tests/neg/tailcall/t6574.scala", defaultOptions) + diff --git a/tests/neg/customArgs/overloadsOnAbstractTypes.scala b/tests/neg/customArgs/overloadsOnAbstractTypes.scala new file mode 100644 index 000000000..0c9ed12bb --- /dev/null +++ b/tests/neg/customArgs/overloadsOnAbstractTypes.scala @@ -0,0 +1,22 @@ + +class Test1 { + type A + type B <: A + + def foo(): A = ??? + def foo(): A = ??? // error + + def bar(): A = ??? + def bar(): B = ??? // error +} + +class Test2 { + type A + type B <: A + + def foo(x: A) = ??? + def foo(x: A) = ??? // error + + def bar(x: A) = ??? + def bar(x: B) = ??? // error +} -- cgit v1.2.3 From 9b398b6a6eaf3cfca2753abdb811b426021156f2 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 12 Apr 2017 15:40:03 +0200 Subject: .drone.yml: Reorder test Since the tests will be run in FIFO order, we can minimize the number of intensive tests running concurrently by putting the intensive tests at the end of the list. --- .drone.yml | 4 ++-- .drone.yml.sig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index c6e4f53e4..22ce99c3a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -40,7 +40,7 @@ pipeline: matrix: TEST: + - dotty-bin-tests/test + - legacyTests - test - ;publishLocal ;dotty-bootstrapped/test - - legacyTests - - dotty-bin-tests/test diff --git a/.drone.yml.sig b/.drone.yml.sig index 830de1a92..e071da484 100644 --- a/.drone.yml.sig +++ b/.drone.yml.sig @@ -1 +1 @@ -eyJhbGciOiJIUzI1NiJ9.IyBBZnRlciB1cGRhdGluZyB0aGlzIGZpbGUsIHlvdSBuZWVkIHRvIHJlLXNpZ24gaXQ6CiMKIyAtIEluc3RhbGwgW2Ryb25lLWNsaV0oaHR0cDovL3JlYWRtZS5kcm9uZS5pby91c2FnZS9nZXR0aW5nLXN0YXJ0ZWQtY2xpLykKIyAtIENvcHkgeW91ciB0b2tlbiBmcm9tICBodHRwOi8vZG90dHktY2kuZXBmbC5jaC9hY2NvdW50IChDbGljayBTSE9XIFRPS0VOKQojIC0gKGV4cG9ydCBEUk9ORV9UT0tFTj15b3VyLXRva2VuOyBleHBvcnQgRFJPTkVfU0VSVkVSPWh0dHA6Ly9kb3R0eS1jaS5lcGZsLmNoOyBkcm9uZSBzaWduIGxhbXBlcGZsL2RvdHR5KQoKcGlwZWxpbmU6CiAgdGVzdDoKICAgIGltYWdlOiBsYW1wZXBmbC9kb3R0eTpsYXRlc3QKICAgIHB1bGw6IHRydWUKICAgIGNvbW1hbmRzOgogICAgICAtIGxuIC1zIC92YXIvY2FjaGUvZHJvbmUvc2NhbGEtc2NhbGEgc2NhbGEtc2NhbGEKICAgICAgLSBsbiAtcyAvdmFyL2NhY2hlL2Ryb25lL2l2eTIgIiRIT01FLy5pdnkyIgogICAgICAtIC4vc2NyaXB0cy91cGRhdGUtc2NhbGEtbGlicmFyeQogICAgICAtIHNidCAtSi1YbXg0MDk2bSAtSi1YWDpSZXNlcnZlZENvZGVDYWNoZVNpemU9NTEybSAtSi1YWDpNYXhNZXRhc3BhY2VTaXplPTEwMjRtIC1EZG90dHkuZHJvbmUubWVtPTQwOTZtICIke1RFU1R9IgogICAgd2hlbjoKICAgICAgYnJhbmNoOgogICAgICAgIGV4Y2x1ZGU6IGdoLXBhZ2VzCgogIGRvY3VtZW50YXRpb246CiAgICBpbWFnZTogbGFtcGVwZmwvZG90dHk6bGF0ZXN0CiAgICBwdWxsOiB0cnVlCiAgICBjb21tYW5kczoKICAgICAgLSAuL3Byb2plY3Qvc2NyaXB0cy9nZW5Eb2NzICIke1RFU1R9IiAkQk9UX1BBU1MKICAgIHdoZW46CiAgICAgIGJyYW5jaDogbWFzdGVyCgogIGdpdHRlcjoKICAgIGltYWdlOiBwbHVnaW5zL2dpdHRlcgogICAgd2hlbjoKICAgICAgYnJhbmNoOiBtYXN0ZXIKICAgICAgc3RhdHVzOiBjaGFuZ2VkCgogIHNsYWNrOgogICAgaW1hZ2U6IHBsdWdpbnMvc2xhY2sKICAgIGNoYW5uZWw6IGRvdHR5CiAgICB3aGVuOgogICAgICBicmFuY2g6IG1hc3RlcgogICAgICBzdGF0dXM6IGNoYW5nZWQKCm1hdHJpeDoKICBURVNUOgogICAgLSB0ZXN0CiAgICAtIDtwdWJsaXNoTG9jYWwgO2RvdHR5LWJvb3RzdHJhcHBlZC90ZXN0CiAgICAtIGxlZ2FjeVRlc3RzCiAgICAtIGRvdHR5LWJpbi10ZXN0cy90ZXN0Cg.7jaA1Gh5FpzKvXQsaf2_of5tUEMBcR_3Mzo0wL8pE3E \ No newline at end of file +eyJhbGciOiJIUzI1NiJ9.IyBBZnRlciB1cGRhdGluZyB0aGlzIGZpbGUsIHlvdSBuZWVkIHRvIHJlLXNpZ24gaXQ6CiMKIyAtIEluc3RhbGwgW2Ryb25lLWNsaV0oaHR0cDovL3JlYWRtZS5kcm9uZS5pby91c2FnZS9nZXR0aW5nLXN0YXJ0ZWQtY2xpLykKIyAtIENvcHkgeW91ciB0b2tlbiBmcm9tICBodHRwOi8vZG90dHktY2kuZXBmbC5jaC9hY2NvdW50IChDbGljayBTSE9XIFRPS0VOKQojIC0gKGV4cG9ydCBEUk9ORV9UT0tFTj15b3VyLXRva2VuOyBleHBvcnQgRFJPTkVfU0VSVkVSPWh0dHA6Ly9kb3R0eS1jaS5lcGZsLmNoOyBkcm9uZSBzaWduIGxhbXBlcGZsL2RvdHR5KQoKcGlwZWxpbmU6CiAgdGVzdDoKICAgIGltYWdlOiBsYW1wZXBmbC9kb3R0eTpsYXRlc3QKICAgIHB1bGw6IHRydWUKICAgIGNvbW1hbmRzOgogICAgICAtIGxuIC1zIC92YXIvY2FjaGUvZHJvbmUvc2NhbGEtc2NhbGEgc2NhbGEtc2NhbGEKICAgICAgLSBsbiAtcyAvdmFyL2NhY2hlL2Ryb25lL2l2eTIgIiRIT01FLy5pdnkyIgogICAgICAtIC4vc2NyaXB0cy91cGRhdGUtc2NhbGEtbGlicmFyeQogICAgICAtIHNidCAtSi1YbXg0MDk2bSAtSi1YWDpSZXNlcnZlZENvZGVDYWNoZVNpemU9NTEybSAtSi1YWDpNYXhNZXRhc3BhY2VTaXplPTEwMjRtIC1EZG90dHkuZHJvbmUubWVtPTQwOTZtICIke1RFU1R9IgogICAgd2hlbjoKICAgICAgYnJhbmNoOgogICAgICAgIGV4Y2x1ZGU6IGdoLXBhZ2VzCgogIGRvY3VtZW50YXRpb246CiAgICBpbWFnZTogbGFtcGVwZmwvZG90dHk6bGF0ZXN0CiAgICBwdWxsOiB0cnVlCiAgICBjb21tYW5kczoKICAgICAgLSAuL3Byb2plY3Qvc2NyaXB0cy9nZW5Eb2NzICIke1RFU1R9IiAkQk9UX1BBU1MKICAgIHdoZW46CiAgICAgIGJyYW5jaDogbWFzdGVyCgogIGdpdHRlcjoKICAgIGltYWdlOiBwbHVnaW5zL2dpdHRlcgogICAgd2hlbjoKICAgICAgYnJhbmNoOiBtYXN0ZXIKICAgICAgc3RhdHVzOiBjaGFuZ2VkCgogIHNsYWNrOgogICAgaW1hZ2U6IHBsdWdpbnMvc2xhY2sKICAgIGNoYW5uZWw6IGRvdHR5CiAgICB3aGVuOgogICAgICBicmFuY2g6IG1hc3RlcgogICAgICBzdGF0dXM6IGNoYW5nZWQKCm1hdHJpeDoKICBURVNUOgogICAgLSBkb3R0eS1iaW4tdGVzdHMvdGVzdAogICAgLSBsZWdhY3lUZXN0cwogICAgLSB0ZXN0CiAgICAtIDtwdWJsaXNoTG9jYWwgO2RvdHR5LWJvb3RzdHJhcHBlZC90ZXN0Cg.bGW0VXWjWrro4w7rn_6Aq2veQaxXr7x3KJJCaF3X8V8 \ No newline at end of file -- cgit v1.2.3 From 8753f0b46d8f7b63fb1640a45bc280624cf495f4 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 12 Apr 2017 16:04:54 +0200 Subject: Replace test alias by test command Aliases are evil and usually don't do what you want. --- project/Build.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 04e75de4c..8b1c0e31e 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -135,7 +135,6 @@ object Build { triggeredMessage in ThisBuild := Watched.clearWhenTriggered, addCommandAlias("run", "dotty-compiler/run") ++ - addCommandAlias("test", "testOnly -- --exclude-categories=java.lang.Exception") ++ addCommandAlias("legacyTests", "dotty-compiler/testOnly dotc.tests") ). settings(publishing) @@ -144,8 +143,7 @@ object Build { lazy val `dotty-bootstrapped` = project. aggregate(`dotty-library-bootstrapped`, `dotty-compiler-bootstrapped`). settings( - publishArtifact := false, - addCommandAlias("test", "testOnly -- --exclude-categories=java.lang.Exception") + publishArtifact := false ) lazy val `dotty-interfaces` = project.in(file("interfaces")). @@ -286,6 +284,11 @@ object Build { ) }.evaluated, + test in Test := { + // Exclude legacy tests by default + (testOnly in Test).toTask(" -- --exclude-categories=java.lang.Exception").value + }, + vulpix := Def.inputTaskDyn { val args: Seq[String] = spaceDelimited("").parsed val cmd = " dotty.tools.dotc.CompilationTests" + { -- cgit v1.2.3 From de5f55dc4ee1bbaa735ab96f1fe810a99c31d081 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 12 Apr 2017 16:05:58 +0200 Subject: TestReporter: Avoid a global lazy val When compiling with dotty, this leads to NullPointerException because the lazy val is not @volatile. Better to just make it a val. --- compiler/test/dotty/tools/dotc/reporting/TestReporter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala index 8645882ca..7952ed728 100644 --- a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala +++ b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala @@ -91,7 +91,7 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M } object TestReporter { - lazy val logWriter = { + val logWriter = { val df = new SimpleDateFormat("yyyy-MM-dd-HH:mm") val timestamp = df.format(new Date) new JFile("../testlogs").mkdirs() -- cgit v1.2.3 From 8f049417a7254564bb69cb5b5296222d5f8157b1 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 13 Apr 2017 09:19:30 +0200 Subject: Update intructions to run a single test I removed the `|` part, it's not possible anymore right? --- docs/docs/contributing/testing.md | 7 +++---- docs/docs/contributing/workflow.md | 15 +++++---------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/docs/docs/contributing/testing.md b/docs/docs/contributing/testing.md index 07aab1918..f786ac233 100644 --- a/docs/docs/contributing/testing.md +++ b/docs/docs/contributing/testing.md @@ -80,10 +80,9 @@ This might be aliased in the future. It is also possible to run tests filtered by using: ```bash -> filterTest .*i2147.scala +> vulpix i2147.scala ``` This will run both the test `./tests/pos/i2147.scala` and -`./tests/partest-test/i2147.scala` since both of these match the given regular -expression. This also means that you could run `filterTest .*` to run all -integration tests. +`./tests/partest-test/i2147.scala` since both of these match the given string. +This also means that you could run `vulpix` with no arguments to run all integration tests. diff --git a/docs/docs/contributing/workflow.md b/docs/docs/contributing/workflow.md index 3c654e8f6..b277cc243 100644 --- a/docs/docs/contributing/workflow.md +++ b/docs/docs/contributing/workflow.md @@ -57,20 +57,15 @@ $ sbt To test a specific test tests/x/y.scala (for example tests/pos/t210.scala): ```bash -> filterTest .*pos/t210.scala +> vulpix pos/t210.scala ``` -The filterTest task takes a regular expression as its argument. For example, -you could run a negative and a positive test with: +The `vulpix` task uses its argument for a substring test. For example, you +could run both a negative and a positive test with the same name +(`pos/i2101.scala` & `neg/i2101.scala`): ```bash -> filterTest (.*pos/t697.scala)|(.*neg/i2101.scala) -``` - -or if they have the same name, the equivalent can be achieved with: - -```bash -> filterTest .*/i2101.scala +> vulpix i2101.scala ``` ## Inspecting Trees with Type Stealer ## -- cgit v1.2.3 From 3e04b6f3aa4e4088220f199bd6aa5c6644c22354 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 13 Apr 2017 09:18:46 +0200 Subject: Revert <: Product requierment in pattern matching The change in question broke the following pattern, commonly used in name based pattern matching: ```scala object ProdEmpty { def _1: Int = ??? def _2: String = ??? def isEmpty = true def get = this } ``` This type define both `_1` and `get` + `isEmpty` (but is not <: Product). After #1938, `ProdEmpty` became eligibles for both product and name based pattern. Because "in case of ambiguities, *Product Pattern* is preferred over *Name Based Pattern*", isEmpty wouldn't be used, breaking the scalac semantics. --- .../src/dotty/tools/dotc/core/Definitions.scala | 4 ++- .../tools/dotc/transform/PatternMatcher.scala | 2 +- .../src/dotty/tools/dotc/typer/Applications.scala | 7 ++-- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/run/1938-2.scala | 37 ++++++++++++++++++++++ 5 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 tests/run/1938-2.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index a97589d73..eee6ba785 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -10,7 +10,6 @@ import scala.collection.{ mutable, immutable } import PartialFunction._ import collection.mutable import util.common.alwaysZero -import typer.Applications object Definitions { @@ -846,6 +845,9 @@ class Definitions { TupleType(elems.size).appliedTo(elems) } + def isProductSubType(tp: Type)(implicit ctx: Context) = + tp.derivesFrom(ProductType.symbol) + /** Is `tp` (an alias) of either a scala.FunctionN or a scala.ImplicitFunctionN? */ def isFunctionType(tp: Type)(implicit ctx: Context) = { val arity = functionArity(tp) diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 41a1218eb..447a003e7 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -1408,7 +1408,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { protected def seqTree(binder: Symbol) = tupleSel(binder)(firstIndexingBinder + 1) protected def tupleSel(binder: Symbol)(i: Int): Tree = { val accessors = - if (Applications.canProductMatch(binder.info)) + if (defn.isProductSubType(binder.info)) productSelectors(binder.info) else binder.caseAccessors val res = diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index c4d3e2292..7e17abbcd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -48,9 +48,6 @@ object Applications { ref.info.widenExpr.dealias } - def canProductMatch(tp: Type)(implicit ctx: Context) = - extractorMemberType(tp, nme._1).exists - /** Does `tp` fit the "product match" conditions as an unapply result type * for a pattern with `numArgs` subpatterns? * This is the case of `tp` has members `_1` to `_N` where `N == numArgs`. @@ -72,7 +69,7 @@ object Applications { } def productArity(tp: Type)(implicit ctx: Context) = - if (canProductMatch(tp)) productSelectorTypes(tp).size else -1 + if (defn.isProductSubType(tp)) productSelectorTypes(tp).size else -1 def productSelectors(tp: Type)(implicit ctx: Context): List[Symbol] = { val sels = for (n <- Iterator.from(0)) yield tp.member(nme.selectorName(n)).symbol @@ -114,7 +111,7 @@ object Applications { getUnapplySelectors(getTp, args, pos) else if (unapplyResult isRef defn.BooleanClass) Nil - else if (canProductMatch(unapplyResult)) + else if (defn.isProductSubType(unapplyResult)) productSelectorTypes(unapplyResult) // this will cause a "wrong number of arguments in pattern" error later on, // which is better than the message in `fail`. diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4bf938fd4..02538671e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -759,7 +759,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit /** Is `formal` a product type which is elementwise compatible with `params`? */ def ptIsCorrectProduct(formal: Type) = { isFullyDefined(formal, ForceDegree.noBottom) && - Applications.canProductMatch(formal) && + defn.isProductSubType(formal) && Applications.productSelectorTypes(formal).corresponds(params) { (argType, param) => param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe diff --git a/tests/run/1938-2.scala b/tests/run/1938-2.scala new file mode 100644 index 000000000..32e4c4518 --- /dev/null +++ b/tests/run/1938-2.scala @@ -0,0 +1,37 @@ +object ProdNonEmpty { + def _1: Int = 0 + def _2: String = "???" // Slight variation with scalac: this test passes + // with ??? here. I think dotty behavior is fine + // according to the spec given that methods involved + // in pattern matching should be pure. + def isEmpty = false + def unapply(s: String): this.type = this + def get = this +} + +object ProdEmpty { + def _1: Int = ??? + def _2: String = ??? + def isEmpty = true + def unapply(s: String): this.type = this + def get = this +} + +object Test { + def main(args: Array[String]): Unit = { + "" match { + case ProdNonEmpty(0, _) => () + case _ => ??? + } + + "" match { + case ProdNonEmpty(1, _) => ??? + case _ => () + } + + "" match { + case ProdEmpty(_, _) => ??? + case _ => () + } + } +} -- cgit v1.2.3 From c9e1bc5b6324d2a896d731eeec32bdd941d1629b Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 13 Apr 2017 09:41:12 +0200 Subject: Fix #2243: improve failing test output --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 4 ++-- compiler/test/dotty/tools/vulpix/ParallelTesting.scala | 8 +++++--- compiler/test/dotty/tools/vulpix/SummaryReport.scala | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index fa0c89f28..ff50d7238 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -216,9 +216,9 @@ class CompilationTests extends ParallelTesting { val opt = Array( "-classpath", // compile with bootstrapped library on cp: - defaultOutputDir + "lib$1/src/:" + + defaultOutputDir + "lib/src/:" + // as well as bootstrapped compiler: - defaultOutputDir + "dotty1$1/dotty/:" + + defaultOutputDir + "dotty1/dotty/:" + Jars.dottyInterfaces ) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index f43462011..1315da3a4 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -265,7 +265,7 @@ trait ParallelTesting extends RunnerOrchestration { self => private[this] val failedTestSources = mutable.ArrayBuffer.empty[String] protected final def failTestSource(testSource: TestSource, reason: Option[String] = None) = synchronized { val extra = reason.map(" with reason: " + _).getOrElse("") - failedTestSources.append(testSource.title + s" failed (in ${testSource.name})" + extra) + failedTestSources.append(testSource.title + s" failed" + extra) fail() } @@ -519,6 +519,7 @@ trait ParallelTesting extends RunnerOrchestration { self => } case Failure(output) => + echo(s"Test '${testSource.title}' failed with output:") echo(output) failTestSource(testSource) @@ -1018,6 +1019,7 @@ trait ParallelTesting extends RunnerOrchestration { self => .getOrElse { throw new IllegalStateException("Unable to reflectively find calling method") } + .takeWhile(_ != '$') } /** Compiles a single file from the string path `f` using the supplied flags */ @@ -1072,7 +1074,7 @@ trait ParallelTesting extends RunnerOrchestration { self => val targetDir = new JFile(outDir + "/" + sourceDir.getName + "/") targetDir.mkdirs() - val target = JointCompilationSource(callingMethod, randomized, flags, targetDir) + val target = JointCompilationSource(s"compiling '$f' in test '$callingMethod'", randomized, flags, targetDir) new CompilationTest(target) } @@ -1089,7 +1091,7 @@ trait ParallelTesting extends RunnerOrchestration { self => targetDir.mkdirs() assert(targetDir.exists, s"couldn't create target directory: $targetDir") - val target = JointCompilationSource(callingMethod, files.map(new JFile(_)).toArray, flags, targetDir) + val target = JointCompilationSource(s"$testName from $callingMethod", files.map(new JFile(_)).toArray, flags, targetDir) // Create a CompilationTest and let the user decide whether to execute a pos or a neg test new CompilationTest(target) diff --git a/compiler/test/dotty/tools/vulpix/SummaryReport.scala b/compiler/test/dotty/tools/vulpix/SummaryReport.scala index 8f3047f49..efca94bba 100644 --- a/compiler/test/dotty/tools/vulpix/SummaryReport.scala +++ b/compiler/test/dotty/tools/vulpix/SummaryReport.scala @@ -95,7 +95,7 @@ final class SummaryReport extends SummaryReporting { startingMessages.foreach(rep.append) - failedTests.map(x => " " + x).foreach(rep.append) + failedTests.map(x => s" $x\n").foreach(rep.append) // If we're compiling locally, we don't need instructions on how to // reproduce failures -- cgit v1.2.3 From 7e33bc4c11ffc5e6ecc6fb7bcbfd54c497db2c02 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 13 Apr 2017 10:09:37 +0200 Subject: Fix #2242: let user know which file got logged to --- .../dotty/tools/dotc/reporting/TestReporter.scala | 28 ++++++++++++++++++---- .../test/dotty/tools/vulpix/SummaryReport.scala | 11 +++++---- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala index 7952ed728..213181b56 100644 --- a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala +++ b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala @@ -91,23 +91,41 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M } object TestReporter { - val logWriter = { + private[this] var outFile: JFile = _ + private[this] var logWriter: PrintWriter = _ + + private[this] def initLog() = if (logWriter eq null) { val df = new SimpleDateFormat("yyyy-MM-dd-HH:mm") val timestamp = df.format(new Date) new JFile("../testlogs").mkdirs() - new PrintWriter(new FileOutputStream(new JFile(s"../testlogs/tests-$timestamp.log"), true)) + outFile = new JFile(s"../testlogs/tests-$timestamp.log") + logWriter = new PrintWriter(new FileOutputStream(outFile, true)) } - def writeToLog(str: String) = { + def logPrintln(str: String) = { + initLog() logWriter.println(str) logWriter.flush() } + def logPrint(str: String): Unit = { + initLog() + logWriter.println(str) + } + + def logFlush(): Unit = + if (logWriter ne null) logWriter.flush() + + def logPath: String = { + initLog() + outFile.getCanonicalPath + } + def reporter(ps: PrintStream, logLevel: Int): TestReporter = - new TestReporter(new PrintWriter(ps, true), writeToLog, logLevel) + new TestReporter(new PrintWriter(ps, true), logPrintln, logLevel) def simplifiedReporter(writer: PrintWriter): TestReporter = { - val rep = new TestReporter(writer, writeToLog, WARNING) { + val rep = new TestReporter(writer, logPrintln, WARNING) { /** Prints the message with the given position indication in a simplified manner */ override def printMessageAndPos(m: MessageContainer, extra: String)(implicit ctx: Context): Unit = { def report() = { diff --git a/compiler/test/dotty/tools/vulpix/SummaryReport.scala b/compiler/test/dotty/tools/vulpix/SummaryReport.scala index efca94bba..81da634f7 100644 --- a/compiler/test/dotty/tools/vulpix/SummaryReport.scala +++ b/compiler/test/dotty/tools/vulpix/SummaryReport.scala @@ -102,10 +102,11 @@ final class SummaryReport extends SummaryReporting { if (isInteractive) { println(rep.toString) if (failed > 0) println { - """| - |---------------------------------------------------------- - |Note: reproduction instructed have been dumped to log file - |----------------------------------------------------------""".stripMargin + s"""| + |-------------------------------------------------------------------------------- + |Note - reproduction instructions have been dumped to log file: + | ${TestReporter.logPath} + |--------------------------------------------------------------------------------""".stripMargin } } @@ -116,7 +117,7 @@ final class SummaryReport extends SummaryReporting { // If we're on the CI, we want everything if (!isInteractive) println(rep.toString) - TestReporter.writeToLog(rep.toString) + TestReporter.logPrintln(rep.toString) // Perform cleanup callback: if (cleanUps.nonEmpty) cleanUps.foreach(_.apply()) -- cgit v1.2.3 From 64db05bc9aa5e82e68209ec4f72b1f87279c8c3a Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 13 Apr 2017 10:10:14 +0200 Subject: Fix #2244: make sure logging goes through appropriate interface --- compiler/test/dotty/tools/vulpix/ParallelTesting.scala | 7 ++----- compiler/test/dotty/tools/vulpix/SummaryReport.scala | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 1315da3a4..355a4046e 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -192,8 +192,6 @@ trait ParallelTesting extends RunnerOrchestration { self => /** A runnable that logs its contents in a buffer */ trait LoggedRunnable extends Runnable { - import TestReporter.logWriter - /** Instances of `LoggedRunnable` implement this method instead of the * `run` method */ @@ -212,8 +210,7 @@ trait ParallelTesting extends RunnerOrchestration { self => final def run(): Unit = { checkTestSource() - logBuffer.iterator.foreach(logWriter.println) - logWriter.flush() + summaryReport.echoToLog(logBuffer.iterator) } } @@ -309,7 +306,7 @@ trait ParallelTesting extends RunnerOrchestration { self => protected def tryCompile(testSource: TestSource)(op: => Unit): Unit = try { val testing = s"Testing ${testSource.title}" - TestReporter.logWriter.println(testing) + summaryReport.echoToLog(testing) if (!isInteractive) realStdout.println(testing) op } catch { diff --git a/compiler/test/dotty/tools/vulpix/SummaryReport.scala b/compiler/test/dotty/tools/vulpix/SummaryReport.scala index 81da634f7..678d88809 100644 --- a/compiler/test/dotty/tools/vulpix/SummaryReport.scala +++ b/compiler/test/dotty/tools/vulpix/SummaryReport.scala @@ -34,6 +34,12 @@ trait SummaryReporting { /** Echo the summary report to the appropriate locations */ def echoSummary(): Unit + + /** Echoes *immediately* to file */ + def echoToLog(msg: String): Unit + + /** Echoes contents of `it` to file *immediately* then flushes */ + def echoToLog(it: Iterator[String]): Unit } /** A summary report that doesn't do anything */ @@ -45,6 +51,8 @@ final class NoSummaryReport extends SummaryReporting { def addStartingMessage(msg: String): Unit = () def addCleanup(f: () => Unit): Unit = () def echoSummary(): Unit = () + def echoToLog(msg: String): Unit = () + def echoToLog(it: Iterator[String]): Unit = () } /** A summary report that logs to both stdout and the `TestReporter.logWriter` @@ -122,6 +130,14 @@ final class SummaryReport extends SummaryReporting { // Perform cleanup callback: if (cleanUps.nonEmpty) cleanUps.foreach(_.apply()) } + + def echoToLog(msg: String): Unit = + TestReporter.logPrintln(msg) + + def echoToLog(it: Iterator[String]): Unit = { + it.foreach(TestReporter.logPrint) + TestReporter.logFlush() + } } object SummaryReport { -- cgit v1.2.3 From 3a15365ab25b6ba0fda2c4321b47f9b257dc5aef Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 13 Apr 2017 10:13:51 +0200 Subject: Fix #2241: print failing test title instead of outdir --- compiler/test/dotty/tools/vulpix/ParallelTesting.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 355a4046e..b0312523d 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -572,7 +572,7 @@ trait ParallelTesting extends RunnerOrchestration { self => if (!compilerCrashed && errorCount == 0) verifier() else { - echo(s"\n Compilation failed for: '$testSource'") + echo(s" Compilation failed for: '${testSource.title}' ") val buildInstr = testSource.buildInstructions(errorCount, warningCount) addFailureInstruction(buildInstr) failTestSource(testSource) -- cgit v1.2.3 From 5b1ae606706e65aa3482367d6d2f30ae230bbadc Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 13 Apr 2017 13:29:19 +0200 Subject: Fix NPE when adding cleanup hook --- compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala b/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala index ad068e9ef..610466224 100644 --- a/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala +++ b/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala @@ -3,6 +3,7 @@ package tools package vulpix import java.io.{ File => JFile, InputStreamReader, BufferedReader, PrintStream } +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.TimeoutException import scala.concurrent.duration.Duration @@ -84,11 +85,11 @@ trait RunnerOrchestration { } /** Did add hook to kill the child VMs? */ - private[this] var didAddCleanupCallback = false + private[this] val didAddCleanupCallback = new AtomicBoolean(false) /** Blocks less than `maxDuration` while running `Test.main` from `dir` */ def runMain(classPath: String)(implicit summaryReport: SummaryReporting): Status = { - if (!didAddCleanupCallback) { + if (didAddCleanupCallback.compareAndSet(false, true)) { // If for some reason the test runner (i.e. sbt) doesn't kill the VM, we // need to clean up ourselves. summaryReport.addCleanup(killAll) -- cgit v1.2.3