diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ConstructorConsistencyChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ConstructorConsistencyChecker.kt index 01630edf2a2..62585a2aaba 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ConstructorConsistencyChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ConstructorConsistencyChecker.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.ReadValueInstruction import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse +import org.jetbrains.kotlin.cfg.variable.PseudocodeVariablesData import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt index e86df6bc17f..988d0023184 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt @@ -19,8 +19,12 @@ package org.jetbrains.kotlin.cfg import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.util.javaslang.ImmutableHashMap import org.jetbrains.kotlin.util.javaslang.ImmutableMap -import org.jetbrains.kotlin.util.javaslang.component1 -import org.jetbrains.kotlin.util.javaslang.component2 + +interface ReadOnlyControlFlowInfo { + fun getOrNull(variableDescriptor: VariableDescriptor): D? + // Only used in tests + fun asMap(): ImmutableMap +} abstract class ControlFlowInfo, D : Any> internal constructor( @@ -51,121 +55,4 @@ internal constructor( override fun hashCode() = map.hashCode() override fun toString() = map.toString() -} - -interface ReadOnlyControlFlowInfo { - fun getOrNull(variableDescriptor: VariableDescriptor): D? - // Only used in tests - fun asMap(): ImmutableMap -} - -interface ReadOnlyInitControlFlowInfo : ReadOnlyControlFlowInfo { - fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean -} - -typealias ReadOnlyUseControlFlowInfo = ReadOnlyControlFlowInfo - -class InitControlFlowInfo(map: ImmutableMap = ImmutableHashMap.empty()) : - ControlFlowInfo(map), ReadOnlyInitControlFlowInfo { - override fun copy(newMap: ImmutableMap) = InitControlFlowInfo(newMap) - - // this = output of EXHAUSTIVE_WHEN_ELSE instruction - // merge = input of MergeInstruction - // returns true if definite initialization in when happens here - override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean { - for ((key, value) in iterator()) { - if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY && - merge.getOrNull(key)?.initState == InitState.INITIALIZED) { - return true - } - } - return false - } -} - -class UseControlFlowInfo(map: ImmutableMap = ImmutableHashMap.empty()) : - ControlFlowInfo(map), ReadOnlyUseControlFlowInfo { - override fun copy(newMap: ImmutableMap) = UseControlFlowInfo(newMap) -} - -enum class InitState(private val s: String) { - // Definitely initialized - INITIALIZED("I"), - // Fake initializer in else branch of "exhaustive when without else", see MagicKind.EXHAUSTIVE_WHEN_ELSE - INITIALIZED_EXHAUSTIVELY("IE"), - // Initialized in some branches, not initialized in other branches - UNKNOWN("I?"), - // Definitely not initialized - NOT_INITIALIZED(""); - - fun merge(other: InitState): InitState { - // X merge X = X - // X merge IE = IE merge X = X - // else X merge Y = I? - if (this == other || other == INITIALIZED_EXHAUSTIVELY) return this - if (this == INITIALIZED_EXHAUSTIVELY) return other - return UNKNOWN - } - - override fun toString() = s -} - -class VariableControlFlowState private constructor(val initState: InitState, val isDeclared: Boolean) { - - fun definitelyInitialized(): Boolean = initState == InitState.INITIALIZED - - fun mayBeInitialized(): Boolean = initState != InitState.NOT_INITIALIZED - - override fun toString(): String { - if (initState == InitState.NOT_INITIALIZED && !isDeclared) return "-" - return "$initState${if (isDeclared) "D" else ""}" - } - - companion object { - - private val VS_IT = VariableControlFlowState(InitState.INITIALIZED, true) - private val VS_IF = VariableControlFlowState(InitState.INITIALIZED, false) - private val VS_ET = VariableControlFlowState(InitState.INITIALIZED_EXHAUSTIVELY, true) - private val VS_EF = VariableControlFlowState(InitState.INITIALIZED_EXHAUSTIVELY, false) - private val VS_UT = VariableControlFlowState(InitState.UNKNOWN, true) - private val VS_UF = VariableControlFlowState(InitState.UNKNOWN, false) - private val VS_NT = VariableControlFlowState(InitState.NOT_INITIALIZED, true) - private val VS_NF = VariableControlFlowState(InitState.NOT_INITIALIZED, false) - - fun create(initState: InitState, isDeclared: Boolean): VariableControlFlowState = - when (initState) { - InitState.INITIALIZED -> if (isDeclared) VS_IT else VS_IF - InitState.INITIALIZED_EXHAUSTIVELY -> if (isDeclared) VS_ET else VS_EF - InitState.UNKNOWN -> if (isDeclared) VS_UT else VS_UF - InitState.NOT_INITIALIZED -> if (isDeclared) VS_NT else VS_NF - } - - fun createInitializedExhaustively(isDeclared: Boolean): VariableControlFlowState = - create(InitState.INITIALIZED_EXHAUSTIVELY, isDeclared) - - fun create(isInitialized: Boolean, isDeclared: Boolean = false): VariableControlFlowState = - create(if (isInitialized) InitState.INITIALIZED else InitState.NOT_INITIALIZED, isDeclared) - - fun create(isDeclaredHere: Boolean, mergedEdgesData: VariableControlFlowState?): VariableControlFlowState = - create(true, isDeclaredHere || mergedEdgesData != null && mergedEdgesData.isDeclared) - } -} - -enum class VariableUseState(private val priority: Int) { - READ(3), - WRITTEN_AFTER_READ(2), - ONLY_WRITTEN_NEVER_READ(1), - UNUSED(0); - - fun merge(variableUseState: VariableUseState?): VariableUseState { - if (variableUseState == null || priority > variableUseState.priority) return this - return variableUseState - } - - companion object { - - @JvmStatic - fun isUsed(variableUseState: VariableUseState?): Boolean = variableUseState != null && variableUseState != UNUSED - } -} - +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt index 42d40d89874..a4ca6d79271 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt @@ -8,7 +8,6 @@ package org.jetbrains.kotlin.cfg import com.intellij.psi.util.PsiTreeUtil.getParentOfType import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.cfg.TailRecursionKind.* -import org.jetbrains.kotlin.cfg.VariableUseState.* import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode @@ -21,6 +20,8 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.MarkInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction import org.jetbrains.kotlin.cfg.pseudocode.sideEffectFree import org.jetbrains.kotlin.cfg.pseudocodeTraverser.* +import org.jetbrains.kotlin.cfg.variable.* +import org.jetbrains.kotlin.cfg.variable.VariableUseState.* import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/PseudocodeVariableDataCollector.kt similarity index 97% rename from compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt rename to compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/PseudocodeVariableDataCollector.kt index 508c9ef94f3..19d9b4060a0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/PseudocodeVariableDataCollector.kt @@ -3,8 +3,9 @@ * that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlin.cfg +package org.jetbrains.kotlin.cfg.variable +import org.jetbrains.kotlin.cfg.ControlFlowInfo import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/PseudocodeVariablesData.kt similarity index 99% rename from compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt rename to compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/PseudocodeVariablesData.kt index a1137f837f4..0bd4c0d8d05 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/PseudocodeVariablesData.kt @@ -3,8 +3,9 @@ * that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlin.cfg +package org.jetbrains.kotlin.cfg.variable +import org.jetbrains.kotlin.cfg.* import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/VariableControlFlowInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/VariableControlFlowInfo.kt new file mode 100644 index 00000000000..e3c7ee80a13 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/variable/VariableControlFlowInfo.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.cfg.variable + +import org.jetbrains.kotlin.cfg.ControlFlowInfo +import org.jetbrains.kotlin.cfg.ImmutableHashMap +import org.jetbrains.kotlin.cfg.ImmutableMap +import org.jetbrains.kotlin.cfg.ReadOnlyControlFlowInfo +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.util.javaslang.component1 +import org.jetbrains.kotlin.util.javaslang.component2 + +interface ReadOnlyInitControlFlowInfo : ReadOnlyControlFlowInfo { + fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean +} + +typealias ReadOnlyUseControlFlowInfo = ReadOnlyControlFlowInfo + +class InitControlFlowInfo(map: ImmutableMap = ImmutableHashMap.empty()) : + ControlFlowInfo(map), ReadOnlyInitControlFlowInfo { + override fun copy(newMap: ImmutableMap) = InitControlFlowInfo(newMap) + + // this = output of EXHAUSTIVE_WHEN_ELSE instruction + // merge = input of MergeInstruction + // returns true if definite initialization in when happens here + override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean { + for ((key, value) in iterator()) { + if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY && + merge.getOrNull(key)?.initState == InitState.INITIALIZED) { + return true + } + } + return false + } +} + +class UseControlFlowInfo(map: ImmutableMap = ImmutableHashMap.empty()) : + ControlFlowInfo(map), ReadOnlyUseControlFlowInfo { + override fun copy(newMap: ImmutableMap) = UseControlFlowInfo(newMap) +} + +enum class InitState(private val s: String) { + // Definitely initialized + INITIALIZED("I"), + // Fake initializer in else branch of "exhaustive when without else", see MagicKind.EXHAUSTIVE_WHEN_ELSE + INITIALIZED_EXHAUSTIVELY("IE"), + // Initialized in some branches, not initialized in other branches + UNKNOWN("I?"), + // Definitely not initialized + NOT_INITIALIZED(""); + + fun merge(other: InitState): InitState { + // X merge X = X + // X merge IE = IE merge X = X + // else X merge Y = I? + if (this == other || other == INITIALIZED_EXHAUSTIVELY) return this + if (this == INITIALIZED_EXHAUSTIVELY) return other + return UNKNOWN + } + + override fun toString() = s +} + +class VariableControlFlowState private constructor(val initState: InitState, val isDeclared: Boolean) { + + fun definitelyInitialized(): Boolean = initState == InitState.INITIALIZED + + fun mayBeInitialized(): Boolean = initState != InitState.NOT_INITIALIZED + + override fun toString(): String { + if (initState == InitState.NOT_INITIALIZED && !isDeclared) return "-" + return "$initState${if (isDeclared) "D" else ""}" + } + + companion object { + + private val VS_IT = VariableControlFlowState(InitState.INITIALIZED, true) + private val VS_IF = VariableControlFlowState(InitState.INITIALIZED, false) + private val VS_ET = VariableControlFlowState(InitState.INITIALIZED_EXHAUSTIVELY, true) + private val VS_EF = VariableControlFlowState(InitState.INITIALIZED_EXHAUSTIVELY, false) + private val VS_UT = VariableControlFlowState(InitState.UNKNOWN, true) + private val VS_UF = VariableControlFlowState(InitState.UNKNOWN, false) + private val VS_NT = VariableControlFlowState(InitState.NOT_INITIALIZED, true) + private val VS_NF = VariableControlFlowState(InitState.NOT_INITIALIZED, false) + + fun create(initState: InitState, isDeclared: Boolean): VariableControlFlowState = + when (initState) { + InitState.INITIALIZED -> if (isDeclared) VS_IT else VS_IF + InitState.INITIALIZED_EXHAUSTIVELY -> if (isDeclared) VS_ET else VS_EF + InitState.UNKNOWN -> if (isDeclared) VS_UT else VS_UF + InitState.NOT_INITIALIZED -> if (isDeclared) VS_NT else VS_NF + } + + fun createInitializedExhaustively(isDeclared: Boolean): VariableControlFlowState = + create(InitState.INITIALIZED_EXHAUSTIVELY, isDeclared) + + fun create(isInitialized: Boolean, isDeclared: Boolean = false): VariableControlFlowState = + create(if (isInitialized) InitState.INITIALIZED else InitState.NOT_INITIALIZED, isDeclared) + + fun create(isDeclaredHere: Boolean, mergedEdgesData: VariableControlFlowState?): VariableControlFlowState = + create(true, isDeclaredHere || mergedEdgesData != null && mergedEdgesData.isDeclared) + } +} + +enum class VariableUseState(private val priority: Int) { + READ(3), + WRITTEN_AFTER_READ(2), + ONLY_WRITTEN_NEVER_READ(1), + UNUSED(0); + + fun merge(variableUseState: VariableUseState?): VariableUseState { + if (variableUseState == null || priority > variableUseState.priority) return this + return variableUseState + } + + companion object { + + @JvmStatic + fun isUsed(variableUseState: VariableUseState?): Boolean = variableUseState != null && variableUseState != UNUSED + } +} \ No newline at end of file diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java b/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java index 58380e00b6e..bfc5a430e88 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java @@ -23,6 +23,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl; import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction; import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges; +import org.jetbrains.kotlin.cfg.variable.PseudocodeVariablesData; +import org.jetbrains.kotlin.cfg.variable.ReadOnlyInitControlFlowInfo; +import org.jetbrains.kotlin.cfg.variable.VariableUseState; import org.jetbrains.kotlin.descriptors.VariableDescriptor; import org.jetbrains.kotlin.resolve.BindingContext;