diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/StrictBasicValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/StrictBasicValue.kt index 8ef9757f6cd..d2597f42e61 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/StrictBasicValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/StrictBasicValue.kt @@ -57,6 +57,9 @@ open class StrictBasicValue(type: Type?) : BasicValue(type) { other as StrictBasicValue + if (this === NULL_VALUE) return other === NULL_VALUE + if (other === NULL_VALUE) return this === NULL_VALUE + if (type != other.type) return false return true diff --git a/compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt b/compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt new file mode 100644 index 00000000000..19dd1927514 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt @@ -0,0 +1,136 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import helpers.* +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +interface AsyncGenerator { + suspend fun yield(value: T) +} + +interface AsyncSequence { + operator fun iterator(): AsyncIterator +} + +interface AsyncIterator { + operator suspend fun hasNext(): Boolean + operator suspend fun next(): T +} + +fun asyncGenerate(block: suspend AsyncGenerator.() -> Unit): AsyncSequence = object : AsyncSequence { + override fun iterator(): AsyncIterator { + val iterator = AsyncGeneratorIterator() + iterator.nextStep = block.createCoroutine(receiver = iterator, completion = iterator) + return iterator + } +} + +class AsyncGeneratorIterator: AsyncIterator, AsyncGenerator, Continuation { + var computedNext = false + var nextValue: T? = null + var nextStep: Continuation? = null + + // if (computesNext) computeContinuation is Continuation + // if (!computesNext) computeContinuation is Continuation + var computesNext = false + var computeContinuation: Continuation<*>? = null + + override val context = EmptyCoroutineContext + + suspend fun computeHasNext(): Boolean = suspendCoroutineOrReturn { c -> + computesNext = false + computeContinuation = c + nextStep!!.resume(Unit) + COROUTINE_SUSPENDED + } + + suspend fun computeNext(): T = suspendCoroutineOrReturn { c -> + computesNext = true + computeContinuation = c + nextStep!!.resume(Unit) + COROUTINE_SUSPENDED + } + + @Suppress("UNCHECKED_CAST") + fun resumeIterator(exception: Throwable?) { + if (exception != null) { + done() + computeContinuation!!.resumeWithException(exception) + return + } + if (computesNext) { + computedNext = false + (computeContinuation as Continuation).resume(nextValue as T) + } else { + (computeContinuation as Continuation).resume(nextStep != null) + } + } + + override suspend fun hasNext(): Boolean { + if (!computedNext) return computeHasNext() + return nextStep != null + } + + override suspend fun next(): T { + if (!computedNext) return computeNext() + computedNext = false + return nextValue as T + } + + private fun done() { + computedNext = true + nextStep = null + } + + // Completion continuation implementation + override fun resume(value: Unit) { + done() + resumeIterator(null) + } + + override fun resumeWithException(exception: Throwable) { + done() + resumeIterator(exception) + } + + // Generator implementation + override suspend fun yield(value: T): Unit = suspendCoroutineOrReturn { c -> + computedNext = true + nextValue = value + nextStep = c + resumeIterator(null) + COROUTINE_SUSPENDED + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun cst(a: Any?): String? = a as String? +fun any(a: Any?): Any? = a + +fun box(): String { + val seq = asyncGenerate { + yield("O") + yield("K") + } + + var res = "" + + builder { + // type of `prev` should be j/l/Object everywhere (even in a expected type position) + var prev: Any? = null + for (i in seq) { + res += i + prev = any(res) + // merge of NULL_VALUE and j/l/Object should result in common j/l/Object value + // but it was NULL_VALUE and we do not spill null values, we just put + // ACONST_NULL after suspension point instead + } + + res = cst(prev) ?: "fail 1" + } + + return res +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 9a47efc8d02..9d5279219a3 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -4982,6 +4982,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("asyncIteratorNullMerge.kt") + public void testAsyncIteratorNullMerge() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt"); + doTest(fileName); + } + @TestMetadata("asyncIteratorToList.kt") public void testAsyncIteratorToList() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index a4e9f2369d8..2f510dcf7cc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4982,6 +4982,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("asyncIteratorNullMerge.kt") + public void testAsyncIteratorNullMerge() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt"); + doTest(fileName); + } + @TestMetadata("asyncIteratorToList.kt") public void testAsyncIteratorToList() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index da20743245c..057d48c4997 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -4982,6 +4982,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("asyncIteratorNullMerge.kt") + public void testAsyncIteratorNullMerge() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt"); + doTest(fileName); + } + @TestMetadata("asyncIteratorToList.kt") public void testAsyncIteratorToList() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 535d9c9a926..33f6b885210 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -5684,6 +5684,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("asyncIteratorNullMerge.kt") + public void testAsyncIteratorNullMerge() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt"); + doTest(fileName); + } + @TestMetadata("asyncIteratorToList.kt") public void testAsyncIteratorToList() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/asyncIteratorToList.kt");