diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt index 82029a97664..1a29657c946 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt @@ -13,12 +13,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.hasDiagnosticKind import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField -import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField -import org.jetbrains.kotlin.fir.declarations.utils.isExternal -import org.jetbrains.kotlin.fir.declarations.utils.isLateInit +import org.jetbrains.kotlin.fir.declarations.utils.* +import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.isCatchParameter import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol @@ -27,10 +26,11 @@ import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph.Kind import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol +import org.jetbrains.kotlin.fir.types.resolvedType object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() { override fun analyze(data: PropertyInitializationInfoData, reporter: DiagnosticReporter, context: CheckerContext) { - data.checkPropertyAccesses(isForClassInitialization = false, context, reporter) + data.checkPropertyAccesses(isForInitialization = false, context, reporter) } } @@ -44,40 +44,44 @@ val FirDeclaration.evaluatedInPlace: Boolean } /** - * [isForClassInitialization] means that caller is interested in member property in the scope - * of class initialization section. In this case the fact that property has initializer does - * not mean that it's safe to access this property in any place: + * [isForInitialization] means that caller is interested in member property in the scope + * of file or class initialization section. In this case the fact that property has + * initializer does not mean that it's safe to access this property in any place: * + * ``` * class A { * val b = a // a is not initialized here * val a = 10 * val c = a // but initialized here * } + * ``` */ - @OptIn(SymbolInternals::class) -fun FirPropertySymbol.requiresInitialization(isForClassInitialization: Boolean): Boolean { +fun FirPropertySymbol.requiresInitialization(isForInitialization: Boolean): Boolean { val hasImplicitBackingField = !hasExplicitBackingField && hasBackingField return when { this is FirSyntheticPropertySymbol -> false - isForClassInitialization -> hasDelegate || hasImplicitBackingField + isForInitialization -> hasDelegate || hasImplicitBackingField else -> !hasInitializer && hasImplicitBackingField && fir.isCatchParameter != true } } fun PropertyInitializationInfoData.checkPropertyAccesses( - isForClassInitialization: Boolean, + isForInitialization: Boolean, context: CheckerContext, reporter: DiagnosticReporter ) { // If a property has an initializer (or does not need one), then any reads are OK while any writes are OK // if it's a `var` and bad if it's a `val`. `FirReassignmentAndInvisibleSetterChecker` does this without a CFG. - val filtered = properties.filterTo(mutableSetOf()) { it.requiresInitialization(isForClassInitialization) } + val filtered = properties.filterTo(mutableSetOf()) { it.requiresInitialization(isForInitialization) } if (filtered.isEmpty()) return checkPropertyAccesses( graph, filtered, context, reporter, scope = null, - isForClassInitialization, doNotReportUninitializedVariable = false, mutableMapOf() + isForInitialization, + doNotReportUninitializedVariable = false, + doNotReportConstantUninitialized = true, + scopes = mutableMapOf(), ) } @@ -88,9 +92,10 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses( context: CheckerContext, reporter: DiagnosticReporter, scope: FirDeclaration?, - isForClassInitialization: Boolean, + isForInitialization: Boolean, doNotReportUninitializedVariable: Boolean, - scopes: MutableMap + doNotReportConstantUninitialized: Boolean, + scopes: MutableMap, ) { fun FirQualifiedAccessExpression.hasCorrectReceiver() = (dispatchReceiver?.unwrapSmartcastExpression() as? FirThisReceiverExpression)?.calleeReference?.boundSymbol == receiver @@ -126,7 +131,9 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses( node is QualifiedAccessNode -> { if (doNotReportUninitializedVariable) continue + if (node.fir.resolvedType.hasDiagnosticKind(DiagnosticKind.RecursionInImplicitTypes)) continue val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue + if (doNotReportConstantUninitialized && symbol.isConst) continue if (!symbol.isLateInit && !symbol.isExternal && node.fir.hasCorrectReceiver() && symbol in properties && getValue(node).values.any { it[symbol]?.isDefinitelyVisited() != true } ) { @@ -144,13 +151,20 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses( * even if they may be not initialized at this point, because if lambda is not in-place, * then it most likely will be called after object will be initialized */ - val doNotReportForSubGraph = doNotReportUninitializedVariable || - (isForClassInitialization && subGraph.kind.doNotReportUninitializedVariableForClassInitialization) + val doNotReportForSubGraph = isForInitialization && subGraph.kind.doNotReportUninitializedVariableForInitialization + + // Must report uninitialized variable if we start initializing a constant property. This + // allows "regular" properties to reference constant properties out-of-order, but all other + // property references must be in-order. + val isSubGraphConstProperty = (subGraph.declaration as? FirProperty)?.isConst == true val newScope = subGraph.declaration?.takeIf { !it.evaluatedInPlace } ?: scope checkPropertyAccesses( subGraph, properties, context, reporter, newScope, - isForClassInitialization, doNotReportForSubGraph, scopes + isForInitialization, + doNotReportUninitializedVariable = doNotReportUninitializedVariable || doNotReportForSubGraph, + doNotReportConstantUninitialized = doNotReportConstantUninitialized && !isSubGraphConstProperty, + scopes ) } } @@ -158,8 +172,8 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses( } } -private val Kind.doNotReportUninitializedVariableForClassInitialization: Boolean +private val Kind.doNotReportUninitializedVariableForInitialization: Boolean get() = when (this) { - Kind.AnonymousFunction, Kind.LocalFunction -> true + Kind.Function, Kind.AnonymousFunction, Kind.LocalFunction -> true else -> false } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index 3a00170cdde..31b9447836d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -23,6 +23,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.getChild import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.* +import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock import org.jetbrains.kotlin.fir.references.FirSuperReference @@ -51,6 +53,8 @@ import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext import org.jetbrains.kotlin.util.ImplementationStatus import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.getChildren +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract private val INLINE_ONLY_ANNOTATION_CLASS_ID: ClassId = ClassId.topLevel(FqName("kotlin.internal.InlineOnly")) @@ -828,4 +832,9 @@ fun ConeKotlinType.leastUpperBound(session: FirSession): ConeKotlinType { fun ConeKotlinType.fullyExpandedClassId(session: FirSession): ClassId? { return fullyExpandedType(session).classId -} \ No newline at end of file +} +@OptIn(ExperimentalContracts::class) +fun ConeKotlinType.hasDiagnosticKind(kind: DiagnosticKind): Boolean { + contract { returns(true) implies (this@hasDiagnosticKind is ConeErrorType) } + return this is ConeErrorType && (diagnostic as? ConeSimpleDiagnostic)?.kind == kind +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDelegatedPropertyChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDelegatedPropertyChecker.kt index 3760ac7291f..b83bcffbe6c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDelegatedPropertyChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDelegatedPropertyChecker.kt @@ -10,9 +10,9 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.hasDiagnosticKind import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.FirProperty -import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirImplicitInvokeCall @@ -33,7 +33,7 @@ object FirDelegatedPropertyChecker : FirPropertyChecker() { if (delegateType is ConeErrorType) { // Implicit recursion type is not reported since the type ref does not have a real source. - if (source != null && (delegateType.diagnostic as? ConeSimpleDiagnostic)?.kind == DiagnosticKind.RecursionInImplicitTypes) { + if (source != null && delegateType.hasDiagnosticKind(DiagnosticKind.RecursionInImplicitTypes)) { // skip reporting other issues in this case reporter.reportOn(source, FirErrors.RECURSION_IN_IMPLICIT_TYPES, context) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMemberPropertiesChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMemberPropertiesChecker.kt index 934182e16da..d2f0da9d536 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMemberPropertiesChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMemberPropertiesChecker.kt @@ -47,12 +47,12 @@ object FirMemberPropertiesChecker : FirClassChecker() { private fun FirClass.collectInitializationInfo(context: CheckerContext, reporter: DiagnosticReporter): PropertyInitializationInfo? { val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null val memberPropertySymbols = declarations.mapNotNullTo(mutableSetOf()) { - (it.symbol as? FirPropertySymbol)?.takeIf { symbol -> symbol.requiresInitialization(isForClassInitialization = true) } + (it.symbol as? FirPropertySymbol)?.takeIf { symbol -> symbol.requiresInitialization(isForInitialization = true) } } if (memberPropertySymbols.isEmpty()) return null // TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes. val data = PropertyInitializationInfoData(memberPropertySymbols, symbol, graph) - data.checkPropertyAccesses(isForClassInitialization = true, context, reporter) + data.checkPropertyAccesses(isForInitialization = true, context, reporter) return data.getValue(graph.exitNode)[NormalPath] } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTopLevelPropertiesChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTopLevelPropertiesChecker.kt index 63fc2d74259..9441f933927 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTopLevelPropertiesChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTopLevelPropertiesChecker.kt @@ -58,13 +58,13 @@ object FirTopLevelPropertiesChecker : FirFileChecker() { } val propertySymbols = topLevelProperties.mapNotNullTo(mutableSetOf()) { declaration -> - (declaration.symbol as? FirPropertySymbol)?.takeIf { it.requiresInitialization(false) } + (declaration.symbol as? FirPropertySymbol)?.takeIf { it.requiresInitialization(isForInitialization = true) } } if (propertySymbols.isEmpty()) return null // TODO, KT-59803: merge with `FirPropertyInitializationAnalyzer` for fewer passes. val data = PropertyInitializationInfoData(propertySymbols, receiver = null, graph) - data.checkPropertyAccesses(isForClassInitialization = false, context, reporter) + data.checkPropertyAccesses(isForInitialization = true, context, reporter) return data.getValue(graph.exitNode)[NormalPath] } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt index b5607be1ed0..5f02915e399 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt @@ -143,11 +143,11 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker() // and `FirTopLevelPropertiesChecker` for top-level properties. if ( (property.isLocal || isInFileGraph(property, context)) - && property.requiresInitialization(isForClassInitialization = false) + && property.requiresInitialization(isForInitialization = false) ) return if ( isInOwnersInitializer(expression.dispatchReceiver?.unwrapSmartcastExpression(), context) - && property.requiresInitialization(isForClassInitialization = true) + && property.requiresInitialization(isForInitialization = true) ) return reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, property, context) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirRecursiveProblemChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirRecursiveProblemChecker.kt index d2098cf4e86..93c3dfc5325 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirRecursiveProblemChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirRecursiveProblemChecker.kt @@ -9,8 +9,8 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.hasDiagnosticKind import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors -import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirLambdaArgumentExpression @@ -28,7 +28,7 @@ object FirRecursiveProblemChecker : FirBasicExpressionChecker() { } fun checkConeType(coneType: ConeKotlinType?) { - if (coneType is ConeErrorType && (coneType.diagnostic as? ConeSimpleDiagnostic)?.kind == DiagnosticKind.RecursionInImplicitTypes) { + if (coneType?.hasDiagnosticKind(DiagnosticKind.RecursionInImplicitTypes) == true) { val source = ((expression as? FirLambdaArgumentExpression)?.expression ?: expression).source reporter.reportOn(source, FirErrors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, context) } else if (coneType is ConeClassLikeType) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeValChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeValChecker.kt index b2c26eb6d03..e59e3226360 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeValChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeValChecker.kt @@ -50,10 +50,11 @@ object CanBeValChecker : AbstractFirPropertyInitializationChecker() { override fun visitVariableAssignmentNode(node: VariableAssignmentNode) { val symbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: return - if (symbol.isVar && symbol.source?.kind !is KtFakeSourceElementKind && symbol in data.properties && - (!symbol.requiresInitialization(isForClassInitialization = data.graph.kind == ControlFlowGraph.Kind.Class) || data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) - ) { - reassigned.add(symbol) + if (symbol.isVar && symbol.source?.kind !is KtFakeSourceElementKind && symbol in data.properties) { + val isForInitialization = data.graph.kind == ControlFlowGraph.Kind.Class || data.graph.kind == ControlFlowGraph.Kind.File + if (!symbol.requiresInitialization(isForInitialization) || data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) { + reassigned.add(symbol) + } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index f7553801678..2a558b17f98 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -187,7 +187,11 @@ class ControlFlowGraphBuilder { val localFunctionNode = runIf(function is FirSimpleFunction && function.isLocal && bodyBuildingMode) { createLocalFunctionDeclarationNode(function).also { addNewSimpleNode(it) } } - val kind = if (localFunctionNode != null) ControlFlowGraph.Kind.LocalFunction else ControlFlowGraph.Kind.Function + val kind = when { + localFunctionNode != null -> ControlFlowGraph.Kind.LocalFunction + function is FirConstructor -> ControlFlowGraph.Kind.Constructor + else -> ControlFlowGraph.Kind.Function + } val enterNode = enterGraph(function, name, kind) { createFunctionEnterNode(it) to createFunctionExitNode(it).also { exit -> exitTargetsForReturn[it.symbol] = exit } } @@ -411,7 +415,8 @@ class ControlFlowGraphBuilder { var lastNode: CFGNode<*> = enterNode for (property in properties) { - addEdge(lastNode, property.enterNode, preferredKind = EdgeKind.CfgForward, propagateDeadness = false) + // Top-level property CFGs should never be linked with dead edges. + CFGNode.addEdge(lastNode, property.enterNode, kind = EdgeKind.CfgForward, propagateDeadness = false) lastNode = property.exitNode } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 931148ddea1..91f4a3814e6 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -34,6 +34,7 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k enum class Kind { File, Class, + Constructor, Function, LocalFunction, AnonymousFunction, diff --git a/compiler/testData/diagnostics/tests/Properties.fir.kt b/compiler/testData/diagnostics/tests/Properties.fir.kt index 9f289318544..a44ec3370ed 100644 --- a/compiler/testData/diagnostics/tests/Properties.fir.kt +++ b/compiler/testData/diagnostics/tests/Properties.fir.kt @@ -1,4 +1,4 @@ -var x : Int = 1 + x +var x : Int = 1 + x get() : Int = 1 set(value : Long) { field = value.toInt() diff --git a/compiler/testData/diagnostics/tests/backingField/TopLevelPropertyInitialization.fir.kt b/compiler/testData/diagnostics/tests/backingField/TopLevelPropertyInitialization.fir.kt index a874a823de5..dac0221132e 100644 --- a/compiler/testData/diagnostics/tests/backingField/TopLevelPropertyInitialization.fir.kt +++ b/compiler/testData/diagnostics/tests/backingField/TopLevelPropertyInitialization.fir.kt @@ -1,31 +1,119 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -WRONG_INVOCATION_KIND // WITH_STDLIB -val a: Int -val b = 1.also { a = 2 } -val c: Int -val d by lazy { c = 2; 1 } -val e: Int - get() { - c = 3 - return c +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.properties.ReadOnlyProperty + +@OptIn(ExperimentalContracts::class) +inline fun inPlaceRun(block: () -> T): T { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return block() +} + +fun notInPlaceRun(block: () -> T): T = null!! + +fun simpleDelegate(value: T): ReadOnlyProperty = null!! + +@OptIn(ExperimentalContracts::class) +fun inPlaceDelegate(block: () -> T): ReadOnlyProperty { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return null!! +} + +fun notInPlaceDelegate(block: () -> T): ReadOnlyProperty = null!! + +class Some { + val a: String = a + val b: String = inPlaceRun { b } + val c: String = notInPlaceRun { c } + + val d: String by simpleDelegate(d) + val e: String by inPlaceDelegate { e } + val f: String by notInPlaceDelegate { f } + + val g: Int + val h = 1.also { g = 2 } + val i: Int + val j by lazy { i = 2; 1 } + val k: Int + get() { + i = 3 + return i + } + + val l: Comparator = object : Comparator { + val delegate: Comparator get() = n + override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2) } -class Class { - val i: Int - val j = 1.also { i = 2 } - val k: Int - val l by lazy { k = 2; 1 } - val n: Int - get() { - k = 3 - return k - } + val m: Comparator = object : Comparator by n {} + + val n: Comparator = Comparator { _, _ -> 0 } + + val t: String = z + val u: String = inPlaceRun { z } + val v: String = notInPlaceRun { z } + val w: String by simpleDelegate(z) + val x: String by inPlaceDelegate { z } + val y: String by notInPlaceDelegate { z } + val z: String = "VALUE" + + companion object { + const val A: String = B + const val B: String = "CONSTANT" + + val tConst: String = Z + val uConst: String = inPlaceRun { Z } + val vConst: String = notInPlaceRun { Z } + val wConst: String by simpleDelegate(Z) + val xConst: String by inPlaceDelegate { Z } + val yConst: String by notInPlaceDelegate { Z } + const val Z: String = "CONSTANT" + } } -fun main() { - val x: Int - val y = 1.also { x = 2 } - val z: Int - val w by lazy { z = 2; 1 } +val a: String = a +val b: String = inPlaceRun { b } +val c: String = notInPlaceRun { c } + +val d: String by simpleDelegate(d) +val e: String by inPlaceDelegate { e } +val f: String by notInPlaceDelegate { f } + +val g: Int +val h = 1.also { g = 2 } +val i: Int +val j by lazy { i = 2; 1 } +val k: Int + get() { + i = 3 + return i + } + +val l: Comparator = object : Comparator { + val delegate: Comparator get() = n + override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2) } +val m: Comparator = object : Comparator by n {} +val n: Comparator = Comparator { _, _ -> 0 } + +val t: String = z +val u: String = inPlaceRun { z } +val v: String = notInPlaceRun { z } +val w: String by simpleDelegate(z) +val x: String by inPlaceDelegate { z } +val y: String by notInPlaceDelegate { z } +val z: String = "VALUE" + +val tConst: String = Z +val uConst: String = inPlaceRun { Z } +val vConst: String = notInPlaceRun { Z } +val wConst: String by simpleDelegate(Z) +val xConst: String by inPlaceDelegate { Z } +val yConst: String by notInPlaceDelegate { Z } +const val Z: String = "CONSTANT" + +const val A: String = B +const val B: String = "CONSTANT" diff --git a/compiler/testData/diagnostics/tests/backingField/TopLevelPropertyInitialization.kt b/compiler/testData/diagnostics/tests/backingField/TopLevelPropertyInitialization.kt index 31107ca04f9..9fe4ea073f4 100644 --- a/compiler/testData/diagnostics/tests/backingField/TopLevelPropertyInitialization.kt +++ b/compiler/testData/diagnostics/tests/backingField/TopLevelPropertyInitialization.kt @@ -1,31 +1,119 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -WRONG_INVOCATION_KIND // WITH_STDLIB -val a: Int -val b = 1.also { a = 2 } -val c: Int -val d by lazy { c = 2; 1 } -val e: Int - get() { - c = 3 - return c +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.properties.ReadOnlyProperty + +@OptIn(ExperimentalContracts::class) +inline fun inPlaceRun(block: () -> T): T { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return block() +} + +fun notInPlaceRun(block: () -> T): T = null!! + +fun simpleDelegate(value: T): ReadOnlyProperty = null!! + +@OptIn(ExperimentalContracts::class) +fun inPlaceDelegate(block: () -> T): ReadOnlyProperty { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return null!! +} + +fun notInPlaceDelegate(block: () -> T): ReadOnlyProperty = null!! + +class Some { + val a: String = a + val b: String = inPlaceRun { b } + val c: String = notInPlaceRun { c } + + val d: String by simpleDelegate(d) + val e: String by inPlaceDelegate { e } + val f: String by notInPlaceDelegate { f } + + val g: Int + val h = 1.also { g = 2 } + val i: Int + val j by lazy { i = 2; 1 } + val k: Int + get() { + i = 3 + return i + } + + val l: Comparator = object : Comparator { + val delegate: Comparator get() = n + override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2) } -class Class { - val i: Int - val j = 1.also { i = 2 } - val k: Int - val l by lazy { k = 2; 1 } - val n: Int - get() { - k = 3 - return k - } + val m: Comparator = object : Comparator by n {} + + val n: Comparator = Comparator { _, _ -> 0 } + + val t: String = z + val u: String = inPlaceRun { z } + val v: String = notInPlaceRun { z } + val w: String by simpleDelegate(z) + val x: String by inPlaceDelegate { z } + val y: String by notInPlaceDelegate { z } + val z: String = "VALUE" + + companion object { + const val A: String = B + const val B: String = "CONSTANT" + + val tConst: String = Z + val uConst: String = inPlaceRun { Z } + val vConst: String = notInPlaceRun { Z } + val wConst: String by simpleDelegate(Z) + val xConst: String by inPlaceDelegate { Z } + val yConst: String by notInPlaceDelegate { Z } + const val Z: String = "CONSTANT" + } } -fun main() { - val x: Int - val y = 1.also { x = 2 } - val z: Int - val w by lazy { z = 2; 1 } +val a: String = a +val b: String = inPlaceRun { b } +val c: String = notInPlaceRun { c } + +val d: String by simpleDelegate(d) +val e: String by inPlaceDelegate { e } +val f: String by notInPlaceDelegate { f } + +val g: Int +val h = 1.also { g = 2 } +val i: Int +val j by lazy { i = 2; 1 } +val k: Int + get() { + i = 3 + return i + } + +val l: Comparator = object : Comparator { + val delegate: Comparator get() = n + override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2) } +val m: Comparator = object : Comparator by n {} +val n: Comparator = Comparator { _, _ -> 0 } + +val t: String = z +val u: String = inPlaceRun { z } +val v: String = notInPlaceRun { z } +val w: String by simpleDelegate(z) +val x: String by inPlaceDelegate { z } +val y: String by notInPlaceDelegate { z } +val z: String = "VALUE" + +val tConst: String = Z +val uConst: String = inPlaceRun { Z } +val vConst: String = notInPlaceRun { Z } +val wConst: String by simpleDelegate(Z) +val xConst: String by inPlaceDelegate { Z } +val yConst: String by notInPlaceDelegate { Z } +const val Z: String = "CONSTANT" + +const val A: String = B +const val B: String = "CONSTANT" diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt index 07863a93c87..5aa7d444718 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt @@ -255,7 +255,7 @@ class Outer() { } class ForwardAccessToBackingField() { //kt-147 - val a = a // error + val a = a // error val b = c // error val c = 1 } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.fir.kt deleted file mode 100644 index 2ca95e2a6a7..00000000000 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.fir.kt +++ /dev/null @@ -1,4 +0,0 @@ -package a - -val a : Int = b -val b : Int = a \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt index ec920b67de7..b4d57274dd8 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt @@ -1,4 +1,5 @@ +// FIR_IDENTICAL package a val a : Int = b -val b : Int = a \ No newline at end of file +val b : Int = a diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/repeatUnitializedErrorOnlyForLocalVars.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/repeatUnitializedErrorOnlyForLocalVars.fir.kt index be28e872c2e..deffcf9c39e 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/repeatUnitializedErrorOnlyForLocalVars.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/repeatUnitializedErrorOnlyForLocalVars.fir.kt @@ -1,7 +1,7 @@ package aa -val a : Int = b -val b : Int = a + b +val a : Int = b +val b : Int = a + b class C { val a : Int = b diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/recursiveType.fir.kt b/compiler/testData/diagnostics/tests/delegatedProperty/recursiveType.fir.kt index 0ca79449c41..7326b2d2b1f 100644 --- a/compiler/testData/diagnostics/tests/delegatedProperty/recursiveType.fir.kt +++ b/compiler/testData/diagnostics/tests/delegatedProperty/recursiveType.fir.kt @@ -7,7 +7,7 @@ val a by b) -val c by d +val c by d val d by c class Delegate(i: Int) { diff --git a/compiler/testData/diagnostics/tests/regressions/Jet81.fir.kt b/compiler/testData/diagnostics/tests/regressions/Jet81.fir.kt index cc9a5448b11..b1ce143d1f7 100644 --- a/compiler/testData/diagnostics/tests/regressions/Jet81.fir.kt +++ b/compiler/testData/diagnostics/tests/regressions/Jet81.fir.kt @@ -3,7 +3,7 @@ class Test { private val y = object { - val a = y; + val a = y; } val z = y.a;