[FIR] Report UNINITIALIZED_VARIABLE for top-level properties

When checking top-level properties, check with initialization enabled.
This makes sure the same errors are reported for member and top-level
properties.
This commit is contained in:
Brian Norman
2023-08-31 11:30:08 -05:00
committed by Space Team
parent 1a35d01fe1
commit 4b6c767704
19 changed files with 300 additions and 97 deletions
@@ -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<FirPropertySymbol, FirDeclaration?>
doNotReportConstantUninitialized: Boolean,
scopes: MutableMap<FirPropertySymbol, FirDeclaration?>,
) {
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
}
@@ -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
}
}
@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
}
@@ -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)
}
@@ -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]
}
}
@@ -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]
}
}
@@ -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)
@@ -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) {
@@ -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)
}
}
}
@@ -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
}
@@ -34,6 +34,7 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
enum class Kind {
File,
Class,
Constructor,
Function,
LocalFunction,
AnonymousFunction,
+1 -1
View File
@@ -1,4 +1,4 @@
var x : Int = 1 + x
var x : Int = 1 + <!UNINITIALIZED_VARIABLE!>x<!>
get() : Int = 1
set(value : <!WRONG_SETTER_PARAMETER_TYPE!>Long<!>) {
field = value.toInt()
@@ -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 }
<!MUST_BE_INITIALIZED!>val c: Int<!>
val d by lazy { <!CAPTURED_VAL_INITIALIZATION!>c<!> = 2; 1 }
val e: Int
get() {
<!VAL_REASSIGNMENT!>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 <T> inPlaceRun(block: () -> T): T {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return block()
}
fun <T> notInPlaceRun(block: () -> T): T = null!!
fun <T> simpleDelegate(value: T): ReadOnlyProperty<Any?, T> = null!!
@OptIn(ExperimentalContracts::class)
fun <T> inPlaceDelegate(block: () -> T): ReadOnlyProperty<Any?, T> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return null!!
}
fun <T> notInPlaceDelegate(block: () -> T): ReadOnlyProperty<Any?, T> = null!!
class Some {
val a: String = <!UNINITIALIZED_VARIABLE!>a<!>
val b: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>b<!> }
val c: String = notInPlaceRun { c }
val d: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>d<!>)
val e: String by inPlaceDelegate { <!UNINITIALIZED_VARIABLE!>e<!> }
val f: String by notInPlaceDelegate { f }
val g: Int
val h = 1.also { g = 2 }
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val i: Int<!>
val j by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>i<!> = 2; 1 }
val k: Int
get() {
<!VAL_REASSIGNMENT!>i<!> = 3
return i
}
val l: Comparator<String> = object : Comparator<String> {
val delegate: Comparator<String> 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 }
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val k: Int<!>
val l by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>k<!> = 2; 1 }
val n: Int
get() {
<!VAL_REASSIGNMENT!>k<!> = 3
return k
}
val m: Comparator<String> = object : Comparator<String> by <!UNINITIALIZED_VARIABLE!>n<!> {}
val n: Comparator<String> = Comparator { _, _ -> 0 }
val t: String = <!UNINITIALIZED_VARIABLE!>z<!>
val u: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>z<!> }
val v: String = notInPlaceRun { z }
val w: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>z<!>)
val x: String by inPlaceDelegate { <!UNINITIALIZED_VARIABLE!>z<!> }
val y: String by notInPlaceDelegate { z }
val z: String = "VALUE"
companion object {
const val A: String = <!UNINITIALIZED_VARIABLE!>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 { <!CAPTURED_VAL_INITIALIZATION!>z<!> = 2; 1 }
val a: String = <!UNINITIALIZED_VARIABLE!>a<!>
val b: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>b<!> }
val c: String = notInPlaceRun { c }
val d: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>d<!>)
val e: String by inPlaceDelegate { <!UNINITIALIZED_VARIABLE!>e<!> }
val f: String by notInPlaceDelegate { f }
val g: Int
val h = 1.also { g = 2 }
<!MUST_BE_INITIALIZED!>val i: Int<!>
val j by lazy { <!CAPTURED_VAL_INITIALIZATION!>i<!> = 2; 1 }
val k: Int
get() {
<!VAL_REASSIGNMENT!>i<!> = 3
return i
}
val l: Comparator<String> = object : Comparator<String> {
val delegate: Comparator<String> get() = n
override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2)
}
val m: Comparator<String> = object : Comparator<String> by <!UNINITIALIZED_VARIABLE!>n<!> {}
val n: Comparator<String> = Comparator { _, _ -> 0 }
val t: String = <!UNINITIALIZED_VARIABLE!>z<!>
val u: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>z<!> }
val v: String = notInPlaceRun { z }
val w: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>z<!>)
val x: String by inPlaceDelegate { <!UNINITIALIZED_VARIABLE!>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 = <!UNINITIALIZED_VARIABLE!>B<!>
const val B: String = "CONSTANT"
@@ -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 }
<!MUST_BE_INITIALIZED!>val c: Int<!>
val d by lazy { c = 2; 1 }
val e: Int
get() {
<!VAL_REASSIGNMENT!>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 <T> inPlaceRun(block: () -> T): T {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return block()
}
fun <T> notInPlaceRun(block: () -> T): T = null!!
fun <T> simpleDelegate(value: T): ReadOnlyProperty<Any?, T> = null!!
@OptIn(ExperimentalContracts::class)
fun <T> inPlaceDelegate(block: () -> T): ReadOnlyProperty<Any?, T> {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
return null!!
}
fun <T> notInPlaceDelegate(block: () -> T): ReadOnlyProperty<Any?, T> = null!!
class Some {
val a: String = <!UNINITIALIZED_VARIABLE!>a<!>
val b: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>b<!> }
val c: String = notInPlaceRun { <!UNINITIALIZED_VARIABLE!>c<!> }
val d: String by simpleDelegate(<!DEBUG_INFO_LEAKING_THIS!>d<!>)
val e: String by inPlaceDelegate { e }
val f: String by notInPlaceDelegate { f }
val g: Int
val h = 1.also { g = 2 }
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val i: Int<!>
val j by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>i<!> = 2; 1 }
val k: Int
get() {
<!VAL_REASSIGNMENT!>i<!> = 3
return i
}
val l: Comparator<String> = object : Comparator<String> {
val delegate: Comparator<String> 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 }
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val k: Int<!>
val l by lazy { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>k<!> = 2; 1 }
val n: Int
get() {
<!VAL_REASSIGNMENT!>k<!> = 3
return k
}
val m: Comparator<String> = object : Comparator<String> by <!UNINITIALIZED_VARIABLE!>n<!> {}
val n: Comparator<String> = Comparator { _, _ -> 0 }
val t: String = <!UNINITIALIZED_VARIABLE!>z<!>
val u: String = inPlaceRun { z }
val v: String = notInPlaceRun { z }
val w: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>z<!>)
val x: String by inPlaceDelegate { z }
val y: String by notInPlaceDelegate { z }
val z: String = "VALUE"
companion object {
const val A: String = <!UNINITIALIZED_VARIABLE!>B<!>
const val B: String = "CONSTANT"
val tConst: String = <!UNINITIALIZED_VARIABLE!>Z<!>
val uConst: String = inPlaceRun { Z }
val vConst: String = notInPlaceRun { Z }
val wConst: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>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 { <!CAPTURED_VAL_INITIALIZATION!>z<!> = 2; 1 }
val a: String = <!UNINITIALIZED_VARIABLE!>a<!>
val b: String = inPlaceRun { <!UNINITIALIZED_VARIABLE!>b<!> }
val c: String = notInPlaceRun { <!UNINITIALIZED_VARIABLE!>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 }
<!MUST_BE_INITIALIZED!>val i: Int<!>
val j by lazy { i = 2; 1 }
val k: Int
get() {
<!VAL_REASSIGNMENT!>i<!> = 3
return i
}
val l: Comparator<String> = object : Comparator<String> {
val delegate: Comparator<String> get() = n
override fun compare(o1: String, o2: String): Int = delegate.compare(o1, o2)
}
val m: Comparator<String> = object : Comparator<String> by <!UNINITIALIZED_VARIABLE!>n<!> {}
val n: Comparator<String> = Comparator { _, _ -> 0 }
val t: String = <!UNINITIALIZED_VARIABLE!>z<!>
val u: String = inPlaceRun { z }
val v: String = notInPlaceRun { z }
val w: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>z<!>)
val x: String by inPlaceDelegate { z }
val y: String by notInPlaceDelegate { z }
val z: String = "VALUE"
val tConst: String = <!UNINITIALIZED_VARIABLE!>Z<!>
val uConst: String = inPlaceRun { Z }
val vConst: String = notInPlaceRun { Z }
val wConst: String by simpleDelegate(<!UNINITIALIZED_VARIABLE!>Z<!>)
val xConst: String by inPlaceDelegate { Z }
val yConst: String by notInPlaceDelegate { Z }
const val Z: String = "CONSTANT"
const val A: String = <!UNINITIALIZED_VARIABLE!>B<!>
const val B: String = "CONSTANT"
@@ -255,7 +255,7 @@ class Outer() {
}
class ForwardAccessToBackingField() { //kt-147
val a = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, UNINITIALIZED_VARIABLE!>a<!> // error
val a = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>a<!> // error
val b = <!UNINITIALIZED_VARIABLE!>c<!> // error
val c = 1
}
@@ -1,4 +0,0 @@
package a
val a : Int = b
val b : Int = a
@@ -1,4 +1,5 @@
// FIR_IDENTICAL
package a
val a : Int = <!UNINITIALIZED_VARIABLE!>b<!>
val b : Int = a
val b : Int = a
@@ -1,7 +1,7 @@
package aa
val a : Int = b
val b : Int = a + b
val a : Int = <!UNINITIALIZED_VARIABLE!>b<!>
val b : Int = a + <!UNINITIALIZED_VARIABLE!>b<!>
class C {
val a : Int = <!UNINITIALIZED_VARIABLE!>b<!>
@@ -7,7 +7,7 @@ val a by <!RECURSION_IN_IMPLICIT_TYPES, TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBL
val b by Delegate(<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>b<!>)
val c by d
val c by <!UNINITIALIZED_VARIABLE!>d<!>
val d by <!RECURSION_IN_IMPLICIT_TYPES, TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>c<!>
class Delegate(i: Int) {
@@ -3,7 +3,7 @@
class Test {
private val y = object {
val a = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM, UNINITIALIZED_VARIABLE!>y<!>;
val a = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>y<!>;
}
val z = <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>y.a<!>;