diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirReturnsImpliesAnalyzer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirReturnsImpliesAnalyzer.kt index 53cc228d7ff..bd1f76c1106 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirReturnsImpliesAnalyzer.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirReturnsImpliesAnalyzer.kt @@ -156,30 +156,34 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() { variableStorage: VariableStorage, flow: Flow, context: CheckerContext - ): MutableTypeStatements? = when (this) { - is ConeBinaryLogicExpression -> { - val left = left.buildTypeStatements(function, logicSystem, variableStorage, flow, context) - val right = right.buildTypeStatements(function, logicSystem, variableStorage, flow, context) - if (left != null && right != null) { - if (kind == LogicOperationKind.AND) { - left.apply { mergeTypeStatements(right) } - } else logicSystem.orForTypeStatements(left, right) - } else (left ?: right) - } - is ConeIsInstancePredicate -> { + ): MutableTypeStatements? { + fun buildTypeStatements(arg: ConeValueParameterReference, exactType: Boolean, type: ConeKotlinType): MutableTypeStatements? { val fir = function.getParameterSymbol(arg.parameterIndex, context).fir val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir) - realVar?.to(simpleTypeStatement(realVar, !isNegated, type))?.let { mutableMapOf(it) } + ?.takeIf { + it.stability == PropertyStability.STABLE_VALUE || + // TODO: consider removing the part below + it.stability == PropertyStability.LOCAL_VAR + } + return realVar?.to(simpleTypeStatement(realVar, exactType, type))?.let { mutableMapOf(it) } } - is ConeIsNullPredicate -> { - val fir = function.getParameterSymbol(arg.parameterIndex, context).fir - val realVar = variableStorage.getOrCreateRealVariable(flow, fir.symbol, fir) - realVar?.to(simpleTypeStatement(realVar, isNegated, context.session.builtinTypes.anyType.type))?.let { mutableMapOf(it) } - } - is ConeLogicalNot -> arg.buildTypeStatements(function, logicSystem, variableStorage, flow, context) - ?.mapValuesTo(mutableMapOf()) { (_, value) -> value.invert() } + return when (this) { + is ConeBinaryLogicExpression -> { + val left = left.buildTypeStatements(function, logicSystem, variableStorage, flow, context) + val right = right.buildTypeStatements(function, logicSystem, variableStorage, flow, context) + if (left != null && right != null) { + if (kind == LogicOperationKind.AND) { + left.apply { mergeTypeStatements(right) } + } else logicSystem.orForTypeStatements(left, right) + } else (left ?: right) + } + is ConeIsInstancePredicate -> buildTypeStatements(arg, !isNegated, type) + is ConeIsNullPredicate -> buildTypeStatements(arg, isNegated, context.session.builtinTypes.anyType.type) + is ConeLogicalNot -> arg.buildTypeStatements(function, logicSystem, variableStorage, flow, context) + ?.mapValuesTo(mutableMapOf()) { (_, value) -> value.invert() } - else -> null + else -> null + } } private fun ConeKotlinType.isInapplicableWith(operation: Operation, session: FirSession): Boolean { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index 2c5e8b5ec2b..61421dd7140 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -10,7 +10,10 @@ import org.jetbrains.kotlin.builtins.functions.FunctionClassKind import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.diagnostics.* +import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.ConeStubDiagnostic +import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.references.FirErrorNamedReference @@ -23,12 +26,12 @@ import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType -import org.jetbrains.kotlin.fir.resolve.providers.* +import org.jetbrains.kotlin.fir.resolve.providers.getSymbolByTypeRef import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolved import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData -import org.jetbrains.kotlin.fir.symbols.* +import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef @@ -247,7 +250,8 @@ private fun BodyResolveComponents.typeFromSymbol(symbol: AbstractFirBasedSymbol< fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo( qualifiedAccessExpression: FirQualifiedAccessExpression ): FirQualifiedAccessExpression { - val typesFromSmartCast = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) ?: return qualifiedAccessExpression + val (stability, typesFromSmartCast) = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression) + ?: return qualifiedAccessExpression val originalType = qualifiedAccessExpression.resultType.coneType // For example, if (x == null) { ... }, // we don't want to smartcast to Nothing?, but we want to record the nullability to its own kind of node. @@ -268,9 +272,11 @@ fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo( return buildExpressionWithSmartcastToNull { originalExpression = qualifiedAccessExpression // TODO: Use Nothing? during resolution? - typeRef = intersectedTypeRefWithoutNullableNothing + smartcastType = intersectedTypeRefWithoutNullableNothing // NB: Nothing? in types from smartcast in DFA is recorded here (and the expression kind itself). this.typesFromSmartCast = typesFromSmartCast + // TODO: differentiate capture local variable + this.smartcastStability = stability.impliedSmartcastStability ?: SmartcastStability.STABLE_VALUE } } val allTypes = typesFromSmartCast.also { @@ -288,7 +294,8 @@ fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo( originalExpression = qualifiedAccessExpression smartcastType = intersectedTypeRef this.typesFromSmartCast = typesFromSmartCast - smartcastStability = SmartcastStability.STABLE_VALUE + // TODO: differentiate capture local variable + this.smartcastStability = stability.impliedSmartcastStability ?: SmartcastStability.STABLE_VALUE } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index 4272d28e9ec..ab25fbd0eec 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -162,7 +162,7 @@ abstract class FirDataFlowAnalyzer( // ----------------------------------- Requests ----------------------------------- - fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): MutableList? { + fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Pair>? { /* * DataFlowAnalyzer holds variables only for declarations that have some smartcast (or can have) * If there is no useful information there is no data flow variable also @@ -170,13 +170,14 @@ abstract class FirDataFlowAnalyzer( val symbol: AbstractFirBasedSymbol<*> = qualifiedAccessExpression.symbol ?: return null val flow = graphBuilder.lastNode.flow var variable = variableStorage.getRealVariableWithoutUnwrappingAlias(symbol, qualifiedAccessExpression, flow) ?: return null + val stability = variable.stability val result = mutableListOf() flow.directAliasMap[variable]?.let { result.addIfNotNull(it.originalType) variable = it.variable } flow.getTypeStatement(variable)?.exactType?.let { result += it } - return result.takeIf { it.isNotEmpty() } + return result.takeIf { it.isNotEmpty() }?.let { stability to it } } fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection { @@ -1009,7 +1010,12 @@ abstract class FirDataFlowAnalyzer( assignment: FirVariableAssignment? ) { val flow = node.flow - val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(flow, property.symbol, assignment ?: property) + val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias( + flow, + property.symbol, + assignment ?: property, + if (property.isVal) PropertyStability.STABLE_VALUE else PropertyStability.LOCAL_VAR + ) val isAssignment = assignment != null if (isAssignment) { logicSystem.removeLocalVariableAlias(flow, propertyVariable) @@ -1017,13 +1023,21 @@ abstract class FirDataFlowAnalyzer( logicSystem.recordNewAssignment(flow, propertyVariable, context.newAssignmentIndex()) } - variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer)?.let { initializerVariable -> - logicSystem.addLocalVariableAlias( - flow, propertyVariable, - RealVariableAndType(initializerVariable, initializer.coneType) - ) - // node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null)) - } + variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer) + ?.let { initializerVariable -> + // TODO: handle capture variable + if ((initializerVariable.stability == PropertyStability.STABLE_VALUE || initializerVariable.stability == PropertyStability.LOCAL_VAR) && + (propertyVariable.stability == PropertyStability.STABLE_VALUE || propertyVariable.stability == PropertyStability.LOCAL_VAR) + ) { + logicSystem.addLocalVariableAlias( + flow, propertyVariable, + RealVariableAndType(initializerVariable, initializer.coneType) + ) + // node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null)) + } else { + logicSystem.replaceVariableFromConditionInStatements(flow, initializerVariable, propertyVariable) + } + } variableStorage.getSyntheticVariable(initializer)?.let { initializerVariable -> /* diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorage.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorage.kt index 4e8854bca5e..5e7114cbd53 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorage.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/VariableStorage.kt @@ -8,7 +8,9 @@ package org.jetbrains.kotlin.fir.resolve.dfa import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor import org.jetbrains.kotlin.fir.declarations.modality import org.jetbrains.kotlin.fir.expressions.* @@ -34,14 +36,24 @@ class VariableStorage(private val session: FirSession) { fun clear(): VariableStorage = VariableStorage(session) - fun getOrCreateRealVariableWithoutUnwrappingAlias(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable { + fun getOrCreateRealVariableWithoutUnwrappingAlias( + flow: Flow, + symbol: AbstractFirBasedSymbol<*>, + fir: FirElement, + stability: PropertyStability + ): RealVariable { val realFir = fir.unwrapElement() val identifier = getIdentifierBySymbol(flow, symbol, realFir) - return _realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir) } + return _realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir, stability) } } - private fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable { - val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir) + private fun getOrCreateRealVariable( + flow: Flow, + symbol: AbstractFirBasedSymbol<*>, + fir: FirElement, + stability: PropertyStability + ): RealVariable { + val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir, stability) return flow.directAliasMap[variable]?.variable ?: variable } @@ -69,7 +81,12 @@ class VariableStorage(private val session: FirSession) { /** * [originalFir] used for extracting expression under and extracting receiver */ - private fun createRealVariableInternal(flow: Flow, identifier: Identifier, originalFir: FirElement): RealVariable { + private fun createRealVariableInternal( + flow: Flow, + identifier: Identifier, + originalFir: FirElement, + stability: PropertyStability + ): RealVariable { val receiver: FirExpression? val isThisReference: Boolean val expression: FirQualifiedAccess? = when (originalFir) { @@ -88,12 +105,12 @@ class VariableStorage(private val session: FirSession) { } val receiverVariable = receiver?.let { getOrCreateVariable(flow, it) } - return RealVariable(identifier, isThisReference, receiverVariable, counter++) + return RealVariable(identifier, isThisReference, receiverVariable, counter++, stability) } @JvmName("getOrCreateRealVariableOrNull") fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>?, fir: FirElement): RealVariable? = - symbol.takeIf { it.isStable(fir) }?.let { getOrCreateRealVariable(flow, it, fir) } + symbol.getStability(fir)?.let { getOrCreateRealVariable(flow, symbol!!, fir, it) } fun createSyntheticVariable(fir: FirElement): SyntheticVariable = SyntheticVariable(fir, counter++).also { syntheticVariables[fir] = it } @@ -101,8 +118,9 @@ class VariableStorage(private val session: FirSession) { fun getOrCreateVariable(flow: Flow, fir: FirElement): DataFlowVariable { val realFir = fir.unwrapElement() val symbol = realFir.symbol - return if (symbol.isStable(realFir)) { - getOrCreateRealVariable(flow, symbol!!, realFir) + val stability = symbol.getStability(realFir) + return if (stability != null) { + getOrCreateRealVariable(flow, symbol!!, realFir, stability) } else { syntheticVariables[realFir] ?: createSyntheticVariable(realFir) } @@ -110,7 +128,7 @@ class VariableStorage(private val session: FirSession) { fun getRealVariableWithoutUnwrappingAlias(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? { val realFir = fir.unwrapElement() - return symbol.takeIf { it.isStable(realFir) }?.let { + return symbol.takeIf { it.getStability(realFir) != null }?.let { _realVariables[getIdentifierBySymbol(flow, it, realFir.unwrapElement())] } } @@ -126,7 +144,8 @@ class VariableStorage(private val session: FirSession) { fun getVariable(fir: FirElement, flow: Flow): DataFlowVariable? { val realFir = fir.unwrapElement() val symbol = realFir.symbol - return if (symbol.isStable(fir)) { + val stability = symbol.getStability(fir) + return if (stability != null) { getRealVariable(symbol, realFir, flow) } else { getSyntheticVariable(fir) @@ -143,39 +162,40 @@ class VariableStorage(private val session: FirSession) { } @OptIn(ExperimentalContracts::class) - fun AbstractFirBasedSymbol<*>?.isStable(originalFir: FirElement): Boolean { + fun AbstractFirBasedSymbol<*>?.getStability(originalFir: FirElement): PropertyStability? { contract { - returns(true) implies(this@isStable != null) + returnsNotNull() implies (this@getStability != null) } when (this) { - is FirAnonymousObjectSymbol -> return false + is FirAnonymousObjectSymbol -> return null is FirFunctionSymbol<*>, is FirClassSymbol<*>, - is FirBackingFieldSymbol -> return true - null -> return false + is FirBackingFieldSymbol -> return PropertyStability.STABLE_VALUE + null -> return null } - if (originalFir is FirThisReceiverExpression) return true - if (this !is FirVariableSymbol<*>) return false + if (originalFir is FirThisReceiverExpression) return PropertyStability.STABLE_VALUE + if (this !is FirVariableSymbol<*>) return null - val property = this.fir as? FirProperty ?: return true + val property = this.fir as? FirProperty ?: return PropertyStability.STABLE_VALUE return when { - property.isLocal -> true - property.isVar -> false - property.receiverTypeRef != null -> false - property.getter.let { it != null && it !is FirDefaultPropertyAccessor } -> false + property.delegate != null -> PropertyStability.DELEGATED_PROPERTY + property.isLocal -> if (property.isVal) PropertyStability.STABLE_VALUE else PropertyStability.LOCAL_VAR + property.isVar -> PropertyStability.MUTABLE_PROPERTY + property.receiverTypeRef != null -> PropertyStability.PROPERTY_WITH_GETTER + property.getter.let { it != null && it !is FirDefaultPropertyAccessor } -> PropertyStability.PROPERTY_WITH_GETTER + property.moduleData.session != session -> PropertyStability.ALIEN_PUBLIC_PROPERTY property.modality != Modality.FINAL -> { - val dispatchReceiver = (originalFir.unwrapElement() as? FirQualifiedAccess)?.dispatchReceiver ?: return false - val receiverType = dispatchReceiver.typeRef.coneTypeSafe()?.fullyExpandedType(session) ?: return false - val receiverSymbol = receiverType.lookupTag.toSymbol(session) ?: return false + val dispatchReceiver = (originalFir.unwrapElement() as? FirQualifiedAccess)?.dispatchReceiver ?: return null + val receiverType = dispatchReceiver.typeRef.coneTypeSafe()?.fullyExpandedType(session) ?: return null + val receiverSymbol = receiverType.lookupTag.toSymbol(session) ?: return null when (val receiverFir = receiverSymbol.fir) { - is org.jetbrains.kotlin.fir.declarations.FirAnonymousObject -> true - is org.jetbrains.kotlin.fir.declarations.FirRegularClass -> receiverFir.modality == Modality.FINAL + is FirAnonymousObject -> PropertyStability.STABLE_VALUE + is FirRegularClass -> if (receiverFir.modality == Modality.FINAL) PropertyStability.STABLE_VALUE else PropertyStability.PROPERTY_WITH_GETTER else -> throw IllegalStateException("Should not be here: $receiverFir") } - } - else -> true + else -> PropertyStability.STABLE_VALUE } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt index 85c18947b66..d808cc66015 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/model.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.types.ConeClassErrorType import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.types.SmartcastStability import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -33,11 +34,37 @@ sealed class DataFlowVariable(private val variableIndexForDebug: Int) { } } +enum class PropertyStability(val impliedSmartcastStability: SmartcastStability?) { + // Immutable and no custom getter or local. + // Smartcast is definitely safe regardless of usage. + STABLE_VALUE(SmartcastStability.STABLE_VALUE), + + // Open or custom getter. + // Smartcast is always unsafe regardless of usage. + PROPERTY_WITH_GETTER(SmartcastStability.PROPERTY_WITH_GETTER), + + // Protected / public member value from another module. + // Smartcast is always unsafe regardless of usage. + ALIEN_PUBLIC_PROPERTY(SmartcastStability.ALIEN_PUBLIC_PROPERTY), + + // Smartcast may or may not be safe, depending on whether there are concurrent writes to this local variable. + LOCAL_VAR(null), + + // Mutable member property of a class or object. + // Smartcast is always unsafe regardless of usage. + MUTABLE_PROPERTY(SmartcastStability.MUTABLE_PROPERTY), + + // Delegated property of a class or object. + // Smartcast is always unsafe regardless of usage. + DELEGATED_PROPERTY(SmartcastStability.DELEGATED_PROPERTY), +} + class RealVariable( val identifier: Identifier, val isThisReference: Boolean, val explicitReceiverVariable: DataFlowVariable?, - variableIndexForDebug: Int + variableIndexForDebug: Int, + val stability: PropertyStability, ) : DataFlowVariable(variableIndexForDebug) { override fun equals(other: Any?): Boolean { return this === other diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt index 0e1db276ab2..7570fb37e71 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt @@ -467,6 +467,8 @@ object NodeConfigurator : AbstractFieldConfigurator(FirTreeBuild +field("originalExpression", qualifiedAccessExpression) +field("typesFromSmartCast", "Collection", null, customType = coneKotlinTypeType) +field("originalType", typeRef) + +field("smartcastType", typeRef) + +smartcastStability } safeCallExpression.configure { diff --git a/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.fir.kt b/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.fir.kt index 145ff09c7ec..1926dacd36d 100644 --- a/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.fir.kt @@ -14,13 +14,13 @@ fun test(u: A?, x: A?, y: A?, z: A?, w: A, v: A?) { u!!.b?.foo()!! x?.b!!.foo()!! // x?.b is not null - x!!.b!!.foo()!! + x!!.b!!.foo()!! y?.nb?.foo()!! y!!.nb?.foo()!! z?.nb!!.foo()!! // z?.nb is not null - z!!.nb!!.foo()!! + z!!.nb!!.foo()!! w.b?.foo()!! w.b!!.foo()!! diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.fir.kt index 3b467593ed3..10449588bdd 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.fir.kt @@ -17,7 +17,7 @@ import a.X public fun X.gav(): Int { if (x != null) // Smart cast is not possible if definition is in another module - return x.length + return x.length else return 0 } @@ -29,7 +29,7 @@ package a public fun X.gav(): Int { if (x != null) // Even if it's in the same package - return x.length + return x.length else return 0 } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt index 501fe32d597..84ee533bd4c 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt @@ -33,7 +33,7 @@ fun case_3(x: Int) = "" fun case_3(x: Int?) = 10 fun case_3() { if (case_3_prop != null) { - val z = case_3(case_3_prop) + val z = case_3(case_3_prop) z } } @@ -89,7 +89,7 @@ fun case_7(x: Int) = "" fun case_7(x: Int?) = 10 fun case_7() { if (case_7_prop != null) { - val z = case_7(case_7_prop) + val z = case_7(case_7_prop) z } }