From 0bf7e6222078c2864ffce4b664afea03e9bdc16e Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 4 May 2022 19:07:29 +0200 Subject: [PATCH 1/2] Simplify lubs in TreeUnpickler A difference between Typer and TreeUnpickler is that Typer performs a simplification step after type assigning a tree mode and unpickler doesn't. This makes a difference for lubs in unsafeNulls mode. We fix this by also doing simplification in Unpickler for trees that can have types resulting from lubs. --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 42 +++++++++++-------- tests/pos/i15097.scala | 13 ++++++ 2 files changed, 38 insertions(+), 17 deletions(-) create mode 100644 tests/pos/i15097.scala diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 0bb4ebd9f772..adba95544214 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -113,6 +113,7 @@ class TreeUnpickler(reader: TastyReader, class Completer(reader: TastyReader)(using @constructorOnly _ctx: Context) extends LazyType { import reader._ val owner = ctx.owner + val mode = ctx.mode val source = ctx.source def complete(denot: SymDenotation)(using Context): Unit = def fail(ex: Throwable) = @@ -129,7 +130,7 @@ class TreeUnpickler(reader: TastyReader, try atPhaseBeforeTransforms { new TreeReader(reader).readIndexedDef()( - using ctx.withOwner(owner).withSource(source)) + using ctx.withOwner(owner).withModeBits(mode).withSource(source)) } catch case ex: AssertionError => fail(ex) @@ -1193,6 +1194,10 @@ class TreeUnpickler(reader: TastyReader, res.withAttachment(SuppressedApplyToNone, ()) else res + def simplifyLub(tree: Tree): Tree = + tree.overwriteType(tree.tpe.simplified) + tree + def readLengthTerm(): Tree = { val end = readEnd() val result = @@ -1231,26 +1236,28 @@ class TreeUnpickler(reader: TastyReader, val expansion = exprReader.readTerm() // need bindings in scope, so needs to be read before Inlined(call, bindings, expansion) case IF => - if (nextByte == INLINE) { - readByte() - InlineIf(readTerm(), readTerm(), readTerm()) - } - else - If(readTerm(), readTerm(), readTerm()) + simplifyLub( + if (nextByte == INLINE) { + readByte() + InlineIf(readTerm(), readTerm(), readTerm()) + } + else + If(readTerm(), readTerm(), readTerm())) case LAMBDA => val meth = readTerm() val tpt = ifBefore(end)(readTpt(), EmptyTree) Closure(Nil, meth, tpt) case MATCH => - if (nextByte == IMPLICIT) { - readByte() - InlineMatch(EmptyTree, readCases(end)) - } - else if (nextByte == INLINE) { - readByte() - InlineMatch(readTerm(), readCases(end)) - } - else Match(readTerm(), readCases(end)) + simplifyLub( + if (nextByte == IMPLICIT) { + readByte() + InlineMatch(EmptyTree, readCases(end)) + } + else if (nextByte == INLINE) { + readByte() + InlineMatch(readTerm(), readCases(end)) + } + else Match(readTerm(), readCases(end))) case RETURN => val from = readSymRef() val expr = ifBefore(end)(readTerm(), EmptyTree) @@ -1258,7 +1265,8 @@ class TreeUnpickler(reader: TastyReader, case WHILE => WhileDo(readTerm(), readTerm()) case TRY => - Try(readTerm(), readCases(end), ifBefore(end)(readTerm(), EmptyTree)) + simplifyLub( + Try(readTerm(), readCases(end), ifBefore(end)(readTerm(), EmptyTree))) case SELECTouter => val levels = readNat() readTerm().outerSelect(levels, SkolemType(readType())) diff --git a/tests/pos/i15097.scala b/tests/pos/i15097.scala new file mode 100644 index 000000000000..94174428e8e4 --- /dev/null +++ b/tests/pos/i15097.scala @@ -0,0 +1,13 @@ +import scala.language.unsafeNulls +class C { + def g: String | Null = ??? + + def f = + try g catch case _ => "" + + def f2 = if ??? then g else "" + + def f3 = (??? : Boolean) match + case true => g + case _ => "" +} \ No newline at end of file From 60d36abd786e1e4765d5bef5a8dad8f2a40a4c9d Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 4 May 2022 19:17:29 +0200 Subject: [PATCH 2/2] Don't simplify lubs when unpickling Ifs I was not able to find out why that's the case, but the original test case with the import in the function body requires that if's are _not_ simplified by Unpickler. The interaction with lubs and unsafenulls seems to be very tricky. Anyway, we now have two versions of all test cases, with local as well as globak unsafe nulls. --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 13 +++++---- tests/pos/i15097.scala | 27 +++++++++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index adba95544214..8696da79c8f3 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1236,13 +1236,12 @@ class TreeUnpickler(reader: TastyReader, val expansion = exprReader.readTerm() // need bindings in scope, so needs to be read before Inlined(call, bindings, expansion) case IF => - simplifyLub( - if (nextByte == INLINE) { - readByte() - InlineIf(readTerm(), readTerm(), readTerm()) - } - else - If(readTerm(), readTerm(), readTerm())) + if (nextByte == INLINE) { + readByte() + InlineIf(readTerm(), readTerm(), readTerm()) + } + else + If(readTerm(), readTerm(), readTerm()) case LAMBDA => val meth = readTerm() val tpt = ifBefore(end)(readTpt(), EmptyTree) diff --git a/tests/pos/i15097.scala b/tests/pos/i15097.scala index 94174428e8e4..8b9cb2b5d893 100644 --- a/tests/pos/i15097.scala +++ b/tests/pos/i15097.scala @@ -1,13 +1,30 @@ -import scala.language.unsafeNulls -class C { +class C: def g: String | Null = ??? def f = + import scala.language.unsafeNulls try g catch case _ => "" - def f2 = if ??? then g else "" + def f2 = + import scala.language.unsafeNulls + if ??? then g else "" - def f3 = (??? : Boolean) match + def f3 = + (??? : Boolean) match + case true => g + case _ => "" + +class C2: + import scala.language.unsafeNulls + def g: String | Null = ??? + + def f = + try g catch case _ => "" + + def f2 = + if ??? then g else "" + + def f3 = + (??? : Boolean) match case true => g case _ => "" -} \ No newline at end of file