From ec794f3578c0ab9363e1cee0f3ff21a56a9d291b Mon Sep 17 00:00:00 2001 From: Xavientois Date: Wed, 2 Feb 2022 08:53:10 -0500 Subject: [PATCH] Add special case to init checker to always allow certain methods Closes #14275 - Treats mehtods `ne`, `eq`, `isInstanceOf`, and `asInstanceOf` as safe to call, regardless of initialization Signed the CLA - @Xavientois Review by @liufengyun --- .../tools/dotc/transform/init/Semantic.scala | 7 ++++++ .../as-instance-of-cold-field-access.scala | 7 ++++++ tests/init/pos/eq-ne-always-valid.scala | 13 +++++++++++ tests/init/pos/instance-of-always-valid.scala | 22 +++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 tests/init/neg/as-instance-of-cold-field-access.scala create mode 100644 tests/init/pos/eq-ne-always-valid.scala create mode 100644 tests/init/pos/instance-of-always-valid.scala diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 417da68a3380..c1e7d6ab9f2a 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -596,8 +596,15 @@ object Semantic { && meth.owner.is(Flags.Module) && meth.owner.companionClass.is(Flags.Case) + def isAlwaysSafe(meth: Symbol) = + (meth eq defn.Object_eq) + || (meth eq defn.Object_ne) + || (meth eq defn.Any_isInstanceOf) + // fast track if the current object is already initialized if promoted.isCurrentObjectPromoted then Result(Hot, Nil) + else if isAlwaysSafe(meth) then Result(Hot, Nil) + else if meth eq defn.Any_asInstanceOf then Result(value, Nil) else value match { case Hot => if isSyntheticApply(meth) then diff --git a/tests/init/neg/as-instance-of-cold-field-access.scala b/tests/init/neg/as-instance-of-cold-field-access.scala new file mode 100644 index 000000000000..ec567326b4eb --- /dev/null +++ b/tests/init/neg/as-instance-of-cold-field-access.scala @@ -0,0 +1,7 @@ +final class MyAsInstanceOfClass(o: MyAsInstanceOfClass) { + val other: MyAsInstanceOfClass = { + if (o.asInstanceOf[MyAsInstanceOfClass].oRef ne null) o // error + else new MyAsInstanceOfClass(this) + } + val oRef = o +} diff --git a/tests/init/pos/eq-ne-always-valid.scala b/tests/init/pos/eq-ne-always-valid.scala new file mode 100644 index 000000000000..8f1348e69587 --- /dev/null +++ b/tests/init/pos/eq-ne-always-valid.scala @@ -0,0 +1,13 @@ +final class MyNeClass(o: MyNeClass) { + val other: MyNeClass = { + if (o ne null) o // o is cold, but ne is always valid + else new MyNeClass(this) + } +} + +final class MyEqClass(o: MyEqClass) { + val other: MyEqClass = { + if (o eq null) new MyEqClass(this) // o is cold, but eq is always valid + else o + } +} diff --git a/tests/init/pos/instance-of-always-valid.scala b/tests/init/pos/instance-of-always-valid.scala new file mode 100644 index 000000000000..5f6f71c5335c --- /dev/null +++ b/tests/init/pos/instance-of-always-valid.scala @@ -0,0 +1,22 @@ +final class MyIsInstanceOfClass(o: MyIsInstanceOfClass) { + val other: MyIsInstanceOfClass = { + if (!o.isInstanceOf[Object]) new MyIsInstanceOfClass(this) // o is cold, but isInstanceOf is always valid + else o + } +} + +final class MyAsInstanceOfClass(o: MyAsInstanceOfClass) { + val other: MyAsInstanceOfClass = { + if (o.asInstanceOf[Object] ne null) o // o is cold, but ne and AsInstanceOf is always valid + else new MyAsInstanceOfClass(this) + } +} + +final class MyAsInstanceOfFieldClass(o: MyAsInstanceOfFieldClass) { + val oRef = o + val other: MyAsInstanceOfFieldClass = { + if (this.asInstanceOf[MyAsInstanceOfFieldClass].oRef ne null) oRef // o is cold, but ne and AsInstanceOf is always valid + else new MyAsInstanceOfFieldClass(this) + } +} +