From 8bac038030e75b295d8623e92adc81c1abd83d1c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Oct 2015 16:26:11 +0100 Subject: [PATCH 1/2] Don't simplify |/& by taking lubs/glbs for explicitly declared types. A lub might return a different type (i.e. singleton types are widenened). We should not do this if the type is given explicitly. --- src/dotty/tools/dotc/ast/tpd.scala | 9 +++++++++ src/dotty/tools/dotc/core/TypeOps.scala | 12 +++++++----- src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 14a36f39848c..52b16262a59a 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -808,6 +808,15 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def tpes: List[Type] = xs map (_.tpe) } + /** Simplify type of tree */ + def simplifyType(tree: Tree)(implicit ctx: Context) = { + def isExplicitType(tree: Tree) = tree match { + case TypeTree(original) => !original.isEmpty + case _ => tree.isType + } + tree.overwriteType(new ctx.SimplifyMap(isExplicitType(tree))(tree.tpe)) + } + // convert a numeric with a toXXX method def primitiveConversion(tree: Tree, numericCls: Symbol)(implicit ctx: Context): Tree = { val mname = ("to" + numericCls.name).toTermName diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 77c6805f01a9..9f6120f533f7 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -243,15 +243,17 @@ trait TypeOps { this: Context => // TODO: Make standalone object. tp.derivedRefinedType(simplify(tp.parent, theMap), tp.refinedName, simplify(tp.refinedInfo, theMap)) case tp: TypeAlias => tp.derivedTypeAlias(simplify(tp.alias, theMap)) - case AndType(l, r) => - simplify(l, theMap) & simplify(r, theMap) - case OrType(l, r) => - simplify(l, theMap) | simplify(r, theMap) + case tp: AndOrType => + val l = simplify(tp.tp1, theMap) + val r = simplify(tp.tp2, theMap) + if (theMap != null && theMap.explicitType) tp.derivedAndOrType(l, r) + else if (tp.isAnd) l & r + else l | r case _ => (if (theMap != null) theMap else new SimplifyMap).mapOver(tp) } - class SimplifyMap extends TypeMap { + class SimplifyMap(val explicitType: Boolean = false) extends TypeMap { def apply(tp: Type) = simplify(tp, this) } diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index e76b2e764a44..e715313373a5 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -825,7 +825,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { } val tree = if (tag < firstLengthTreeTag) readSimpleTerm() else readLengthTerm() - tree.overwriteType(tree.tpe.simplified) + simplifyType(tree) setPos(start, tree) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 52ea32bbc1be..b3f6c148cb38 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1267,7 +1267,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit /*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ { if (tree.isDef) interpolateUndetVars(tree, tree.symbol) else if (!tree.tpe.widen.isInstanceOf[MethodOrPoly]) interpolateUndetVars(tree, NoSymbol) - tree.overwriteType(tree.tpe.simplified) + simplifyType(tree) adaptInterpolated(tree, pt, original) } } From 9d0678204e8735802aac33c50b5268b790c874b3 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 30 Oct 2015 10:12:18 +0100 Subject: [PATCH 2/2] Test that singletons in unions are preserved when explicitly written in the code --- test/dotc/tests.scala | 1 + tests/neg/singletons-lubs.scala | 7 +++++++ tests/pos/singletons-lubs.scala | 12 ++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/neg/singletons-lubs.scala create mode 100644 tests/pos/singletons-lubs.scala diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 337285c04d15..91c615c1d80c 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -160,6 +160,7 @@ class tests extends CompilerTest { @Test def neg_validate = compileFile(negDir, "validate", xerrors = 18) @Test def neg_validateParsing = compileFile(negDir, "validate-parsing", xerrors = 7) @Test def neg_validateRefchecks = compileFile(negDir, "validate-refchecks", xerrors = 2) + @Test def neg_singletonsLubs = compileFile(negDir, "singletons-lubs", xerrors = 2) @Test def run_all = runFiles(runDir) diff --git a/tests/neg/singletons-lubs.scala b/tests/neg/singletons-lubs.scala new file mode 100644 index 000000000000..3a84cad61df9 --- /dev/null +++ b/tests/neg/singletons-lubs.scala @@ -0,0 +1,7 @@ +object Test { + def oneOrTwo(x: 1 | 2): 1 | 2 = x + def test: Unit = { + val foo: 3 | 4 = 1 // error + oneOrTwo(foo) // error + } +} diff --git a/tests/pos/singletons-lubs.scala b/tests/pos/singletons-lubs.scala new file mode 100644 index 000000000000..385c17fdbac0 --- /dev/null +++ b/tests/pos/singletons-lubs.scala @@ -0,0 +1,12 @@ +object Test { + def oneOrTwo(x: 1 | 2): 1 | 2 = x + def test: Unit = { + val foo: 1 | 2 = 1 + val bar: 1 | 2 = foo + oneOrTwo(oneOrTwo(foo)) + 1 match { + case x: (1 | 2) => oneOrTwo(x) + //case x @ (1 | 2) => oneOrTwo(x) // disallowed to avoid deep subtyping checks + } + } +}