From 62f7e8f71f08dd2816b0cd7cf272c8787e2482e8 Mon Sep 17 00:00:00 2001 From: Tianyu Geng Date: Fri, 7 May 2021 21:20:17 -0700 Subject: [PATCH] FIR DFA: store stability in RealVariable As part of this change, we also extend the usage of RealVariable in more places during DFA. Now mutable properties, property with custom getters, delegated properties, etc are also treatd as a `RealVariable`. In general this is needed in order to carry out smartcast computation in order to report `SMARTCAST_IMPOSSIBLE`. It seems to also have side effects that improves behavior of some test files. --- .../analysis/cfa/FirReturnsImpliesAnalyzer.kt | 44 +++++----- .../kotlin/fir/resolve/ResolveUtils.kt | 19 +++-- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 34 +++++--- .../kotlin/fir/resolve/dfa/VariableStorage.kt | 80 ++++++++++++------- .../jetbrains/kotlin/fir/resolve/dfa/model.kt | 29 ++++++- .../fir/tree/generator/NodeConfigurator.kt | 2 + .../nestedCalls/makeNullableIfSafeCall.fir.kt | 4 +- .../smartCasts/publicVals/otherModule.fir.kt | 4 +- .../diagnostics/notLinked/dfa/pos/53.fir.kt | 4 +- 9 files changed, 147 insertions(+), 73 deletions(-) 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 } }