[FIR] Don't save DFA implications for unstable local vars
^KT-57502 Fixed
This commit is contained in:
committed by
Space Team
parent
31424e38ac
commit
40b8b682f9
+6
@@ -31287,6 +31287,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/localDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("objectMembers.kt")
|
||||
public void testObjectMembers() throws Exception {
|
||||
|
||||
+6
@@ -4771,6 +4771,12 @@ public class DiagnosticCompilerTestFirTestdataTestGenerated extends AbstractDiag
|
||||
public void testSmartcastAfterReassignment() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastAfterReassignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smartcastStoredInLocalVar.kt")
|
||||
public void testSmartcastStoredInLocalVar() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastStoredInLocalVar.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -31287,6 +31287,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/localDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("objectMembers.kt")
|
||||
public void testObjectMembers() throws Exception {
|
||||
|
||||
+6
@@ -4771,6 +4771,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated
|
||||
public void testSmartcastAfterReassignment() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastAfterReassignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smartcastStoredInLocalVar.kt")
|
||||
public void testSmartcastStoredInLocalVar() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastStoredInLocalVar.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -4230,6 +4230,11 @@ public class LazyBodyIsNotTouchedTestGenerated extends AbstractLazyBodyIsNotTouc
|
||||
public void testSmartcastAfterReassignment() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastAfterReassignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("smartcastStoredInLocalVar.kt")
|
||||
public void testSmartcastStoredInLocalVar() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastStoredInLocalVar.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
FILE: smartcastStoredInLocalVar.kt
|
||||
public final fun nonInPlaceRun(block: R|() -> kotlin/Unit|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun test_0(a: R|kotlin/Any|): R|kotlin/Unit| {
|
||||
lvar b: R|kotlin/Boolean| = (R|<local>/a| is R|kotlin/String|)
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
R|/nonInPlaceRun|(<L> = nonInPlaceRun@fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun test_1(a: R|kotlin/Any|): R|kotlin/Unit| {
|
||||
lvar b: R|kotlin/Boolean| = (R|<local>/a| is R|kotlin/String|)
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
R|/nonInPlaceRun|(<L> = nonInPlaceRun@fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
R|<local>/b| = Boolean(true)
|
||||
}
|
||||
public final fun test_2(a: R|kotlin/Any|): R|kotlin/Unit| {
|
||||
lvar b: R|kotlin/Boolean| = (R|<local>/a| is R|kotlin/String|)
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
R|/nonInPlaceRun|(<L> = nonInPlaceRun@fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
R|<local>/b| = Boolean(true)
|
||||
}
|
||||
)
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun test_3(a: R|kotlin/Any|): R|kotlin/Unit| {
|
||||
lvar b: R|kotlin/Boolean| = (R|<local>/a| is R|kotlin/String|)
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
R|kotlin/run|<R|kotlin/Unit|>(<L> = run@fun <anonymous>(): R|kotlin/Unit| <inline=Inline, kind=EXACTLY_ONCE> {
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
R|<local>/b| = Boolean(true)
|
||||
}
|
||||
public final fun test_4(a: R|kotlin/Any|): R|kotlin/Unit| {
|
||||
lvar b: R|kotlin/Boolean| = (R|<local>/a| is R|kotlin/String|)
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
R|kotlin/run|<R|kotlin/Unit|>(<L> = run@fun <anonymous>(): R|kotlin/Unit| <inline=Inline, kind=EXACTLY_ONCE> {
|
||||
R|<local>/b| = Boolean(true)
|
||||
}
|
||||
)
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun test_5(a: R|kotlin/Any|): R|kotlin/Unit| {
|
||||
lvar b: R|kotlin/Boolean| = (R|<local>/a| is R|kotlin/String|)
|
||||
while(R|<local>/b|) {
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
R|<local>/b| = (R|<local>/a| is R|kotlin/String|)
|
||||
}
|
||||
|
||||
when () {
|
||||
R|<local>/b| -> {
|
||||
R|<local>/a|.<Unresolved name: length>#
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
// WITH_STDLIB
|
||||
// ISSUE: KT-58604
|
||||
|
||||
fun nonInPlaceRun(block: () -> Unit) {}
|
||||
|
||||
fun test_0(a: Any) {
|
||||
var b = a is String
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // can be ok
|
||||
}
|
||||
nonInPlaceRun {
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // can be ok
|
||||
}
|
||||
}
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // can be ok
|
||||
}
|
||||
}
|
||||
|
||||
fun test_1(a: Any) {
|
||||
var b = a is String
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // can be ok
|
||||
}
|
||||
nonInPlaceRun {
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // not ok
|
||||
}
|
||||
}
|
||||
b = true
|
||||
}
|
||||
|
||||
fun test_2(a: Any) {
|
||||
var b = a is String
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // can be ok
|
||||
}
|
||||
nonInPlaceRun {
|
||||
b = true
|
||||
}
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // not ok
|
||||
}
|
||||
}
|
||||
|
||||
fun test_3(a: Any) {
|
||||
var b = a is String
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // // can be ok
|
||||
}
|
||||
run {
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // // can be ok
|
||||
}
|
||||
}
|
||||
b = true
|
||||
}
|
||||
|
||||
fun test_4(a: Any) {
|
||||
var b = a is String
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // // can be ok
|
||||
}
|
||||
run {
|
||||
b = true
|
||||
}
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // not ok
|
||||
}
|
||||
}
|
||||
|
||||
fun test_5(a: Any) {
|
||||
var b = a is String
|
||||
while (b) {
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // not ok
|
||||
}
|
||||
b = a is String
|
||||
}
|
||||
if (b) {
|
||||
a.<!UNRESOLVED_REFERENCE!>length<!> // // can be ok
|
||||
}
|
||||
}
|
||||
+6
@@ -4771,6 +4771,12 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi
|
||||
public void testSmartcastAfterReassignment() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastAfterReassignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smartcastStoredInLocalVar.kt")
|
||||
public void testSmartcastStoredInLocalVar() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastStoredInLocalVar.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -31287,6 +31287,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/localDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("objectMembers.kt")
|
||||
public void testObjectMembers() throws Exception {
|
||||
|
||||
+6
@@ -4771,6 +4771,12 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest
|
||||
public void testSmartcastAfterReassignment() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastAfterReassignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("smartcastStoredInLocalVar.kt")
|
||||
public void testSmartcastStoredInLocalVar() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/variables/smartcastStoredInLocalVar.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -31383,6 +31383,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/localDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("objectMembers.kt")
|
||||
public void testObjectMembers() throws Exception {
|
||||
|
||||
+7
-8
@@ -919,11 +919,8 @@ abstract class FirDataFlowAnalyzer(
|
||||
assignmentLhs: FirExpression?,
|
||||
hasExplicitType: Boolean,
|
||||
) {
|
||||
val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAlias(
|
||||
flow,
|
||||
property.symbol,
|
||||
assignmentLhs ?: property,
|
||||
if (property.isVal) PropertyStability.STABLE_VALUE else PropertyStability.LOCAL_VAR
|
||||
val propertyVariable = variableStorage.getOrCreateRealVariableWithoutUnwrappingAliasForPropertyInitialization(
|
||||
flow, property.symbol, assignmentLhs ?: property
|
||||
)
|
||||
val isAssignment = assignmentLhs != null
|
||||
if (isAssignment) {
|
||||
@@ -932,8 +929,7 @@ abstract class FirDataFlowAnalyzer(
|
||||
|
||||
val initializerVariable = variableStorage.getOrCreateIfReal(flow, initializer)
|
||||
if (initializerVariable is RealVariable) {
|
||||
val isInitializerStable =
|
||||
initializerVariable.isStable || (initializerVariable.hasLocalStability && !isAccessToUnstableLocalVariable(initializer))
|
||||
val isInitializerStable = initializerVariable.isStableOrLocalStableAccess(initializer)
|
||||
if (!hasExplicitType && isInitializerStable && (propertyVariable.hasLocalStability || propertyVariable.isStable)) {
|
||||
// val a = ...
|
||||
// val b = a
|
||||
@@ -945,7 +941,7 @@ abstract class FirDataFlowAnalyzer(
|
||||
// if (b != null) { /* a != null, but a.x could have changed */ }
|
||||
logicSystem.translateVariableFromConditionInStatements(flow, initializerVariable, propertyVariable)
|
||||
}
|
||||
} else if (initializerVariable != null) {
|
||||
} else if (initializerVariable != null && propertyVariable.isStable) {
|
||||
// val b = x is String
|
||||
// if (b) { /* x is String */ }
|
||||
logicSystem.translateVariableFromConditionInStatements(flow, initializerVariable, propertyVariable)
|
||||
@@ -961,6 +957,9 @@ abstract class FirDataFlowAnalyzer(
|
||||
private val RealVariable.isStable get() = stability == PropertyStability.STABLE_VALUE
|
||||
private val RealVariable.hasLocalStability get() = stability == PropertyStability.LOCAL_VAR
|
||||
|
||||
private fun RealVariable.isStableOrLocalStableAccess(access: FirExpression): Boolean {
|
||||
return isStable || (hasLocalStability && !isAccessToUnstableLocalVariable(access))
|
||||
}
|
||||
|
||||
fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {
|
||||
graphBuilder.exitThrowExceptionNode(throwExpression).mergeIncomingFlow()
|
||||
|
||||
+3
-3
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.fir.expressions.explicitReceiver
|
||||
|
||||
@OptIn(DfaInternals::class)
|
||||
class VariableStorageImpl(private val session: FirSession) : VariableStorage() {
|
||||
@@ -38,14 +37,15 @@ class VariableStorageImpl(private val session: FirSession) : VariableStorage() {
|
||||
|
||||
fun clear(): VariableStorageImpl = VariableStorageImpl(session)
|
||||
|
||||
fun getOrCreateRealVariableWithoutUnwrappingAlias(
|
||||
fun getOrCreateRealVariableWithoutUnwrappingAliasForPropertyInitialization(
|
||||
flow: Flow,
|
||||
symbol: FirBasedSymbol<*>,
|
||||
fir: FirElement,
|
||||
stability: PropertyStability
|
||||
): RealVariable {
|
||||
val realFir = fir.unwrapElement()
|
||||
val identifier = getIdentifierBySymbol(flow, symbol, realFir)
|
||||
val stability = symbol.getStability(fir)
|
||||
requireNotNull(stability) { "Stability for initialized variable always should be computable" }
|
||||
return _realVariables[identifier] ?: createReal(flow, identifier, realFir, stability)
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// ISSUE: KT-57502
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
fun test(foo: Any) {
|
||||
var test by Delegates.observable(true) { property, oldValue, newValue -> }
|
||||
test = foo is String
|
||||
if (test) {
|
||||
foo.<!UNRESOLVED_REFERENCE!>length<!> // no smartcast
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -15,6 +15,6 @@ fun foo(arg: Int?) {
|
||||
var z = arg
|
||||
z = z?.let { 42 }
|
||||
if (z != null) {
|
||||
arg.hashCode()
|
||||
arg<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+6
@@ -32155,6 +32155,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/lambdaBetweenArguments.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/variables/localDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("objectMembers.kt")
|
||||
public void testObjectMembers() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user