Skip to content

Commit e011994

Browse files
lunakolySpace Team
authored and
Space Team
committed
[FIR] Report the uses of compareAnd* methods, not all atomics to primitives
Some operations on such atomics are still valid, so this change prevents `@Suppress` that one would consider adding if they want to use those operations. ^KT-73508
1 parent b58a9a5 commit e011994

File tree

30 files changed

+212
-313
lines changed

30 files changed

+212
-313
lines changed

compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/commonAtomicTypes/overrideWithRawType.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import kotlin.concurrent.atomics.AtomicReference
1010

1111
open class KotlinClass {
12-
open fun foo(a: <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicReference<Int><!>) { }
12+
open fun foo(a: AtomicReference<Int>) { }
1313
}
1414

1515
// FILE: JavaClass.java
@@ -28,5 +28,5 @@ import kotlin.concurrent.atomics.AtomicReference
2828

2929
fun usage(a: JavaClass) {
3030
a.foo(java.util.concurrent.atomic.AtomicReference(""))
31-
a.foo(<!ARGUMENT_TYPE_MISMATCH, ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicReference(1)<!>)
31+
a.foo(<!ARGUMENT_TYPE_MISMATCH!>AtomicReference(1)<!>)
3232
}

compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt

-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import org.jetbrains.kotlin.fir.analysis.checkers.type.FirResolvedTypeRefChecker
1111
import org.jetbrains.kotlin.fir.analysis.checkers.type.TypeCheckers
1212
import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirArrayOfNullableNothingTypeChecker
1313
import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirFunctionalTypeParameterNameChecker
14-
import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirJvmAtomicReferenceArrayToPrimitiveTypeChecker
15-
import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirJvmAtomicReferenceToPrimitiveTypeChecker
1614
import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirJvmModuleAccessibilityTypeChecker
1715

1816
object JvmTypeCheckers : TypeCheckers() {
@@ -23,8 +21,6 @@ object JvmTypeCheckers : TypeCheckers() {
2321
override val resolvedTypeRefCheckers: Set<FirResolvedTypeRefChecker> = setOf(
2422
FirDynamicUnsupportedChecker,
2523
FirJvmModuleAccessibilityTypeChecker,
26-
FirJvmAtomicReferenceToPrimitiveTypeChecker,
27-
FirJvmAtomicReferenceArrayToPrimitiveTypeChecker,
2824
FirArrayOfNullableNothingTypeChecker,
2925
)
3026
}

compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmAtomicReferenceToPrimitiveCallChecker.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import org.jetbrains.kotlin.name.JvmStandardClassIds
1111

1212
object FirJvmAtomicReferenceToPrimitiveCallChecker :
1313
AbstractAtomicReferenceToPrimitiveCallChecker(
14-
JvmStandardClassIds.ATOMIC_REFERENCE_CLASS_ID,
1514
JvmStandardClassIds.atomicByPrimitive,
1615
MppCheckerKind.Platform,
16+
JvmStandardClassIds.Callables.atomicReferenceCompareAndSet,
17+
JvmStandardClassIds.Callables.atomicReferenceCompareAndExchange,
1718
)
1819

1920
object FirJvmAtomicReferenceArrayToPrimitiveCallChecker :
2021
AbstractAtomicReferenceToPrimitiveCallChecker(
21-
JvmStandardClassIds.ATOMIC_REFERENCE_ARRAY_CLASS_ID,
2222
JvmStandardClassIds.atomicArrayByPrimitive,
2323
MppCheckerKind.Platform,
24+
JvmStandardClassIds.Callables.atomicReferenceArrayCompareAndSet,
25+
JvmStandardClassIds.Callables.atomicReferenceArrayCompareAndExchange,
2426
)

compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/type/FirJvmAtomicReferenceToPrimitiveTypeChecker.kt

-24
This file was deleted.

compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeAtomicReferenceToPrimitiveCallChecker.kt

+5-8
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,18 @@ package org.jetbrains.kotlin.fir.analysis.native.checkers
77

88
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
99
import org.jetbrains.kotlin.fir.analysis.checkers.expression.AbstractAtomicReferenceToPrimitiveCallChecker
10-
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
11-
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
1210
import org.jetbrains.kotlin.name.NativeRuntimeNames
1311

1412
object FirNativeAtomicReferenceToPrimitiveCallChecker : AbstractAtomicReferenceToPrimitiveCallChecker(
15-
NativeRuntimeNames.AtomicReference,
1613
NativeRuntimeNames.atomicByPrimitive,
1714
MppCheckerKind.Platform,
15+
NativeRuntimeNames.Callables.atomicReferenceCompareAndSet,
16+
NativeRuntimeNames.Callables.atomicReferenceCompareAndExchange,
1817
)
1918

2019
object FirNativeAtomicArrayToPrimitiveCallChecker : AbstractAtomicReferenceToPrimitiveCallChecker(
21-
NativeRuntimeNames.AtomicArray,
2220
NativeRuntimeNames.atomicArrayByPrimitive,
2321
MppCheckerKind.Platform,
24-
) {
25-
override val FirBasedSymbol<*>.canInstantiateProblematicAtomicReference: Boolean
26-
get() = this is FirFunctionSymbol<*> && callableId == NativeRuntimeNames.Callables.AtomicArray
27-
}
22+
NativeRuntimeNames.Callables.atomicArrayCompareAndSet,
23+
NativeRuntimeNames.Callables.atomicArrayCompareAndExchange,
24+
)

compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/FirNativeAtomicReferenceToPrimitiveTypeChecker.kt

-24
This file was deleted.

compiler/fir/checkers/checkers.native/src/org/jetbrains/kotlin/fir/analysis/native/checkers/NativeTypeCheckers.kt

-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import org.jetbrains.kotlin.fir.analysis.checkers.type.*
1010
object NativeTypeCheckers : TypeCheckers() {
1111
override val resolvedTypeRefCheckers: Set<FirResolvedTypeRefChecker>
1212
get() = setOf(
13-
FirNativeAtomicReferenceToPrimitiveTypeChecker,
14-
FirNativeAtomicArrayToPrimitiveTypeChecker,
1513
FirDynamicUnsupportedChecker,
1614
)
1715
}

compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonTypeCheckers.kt

-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ object CommonTypeCheckers : TypeCheckers() {
2525
FirContextualFunctionTypeChecker,
2626
FirKotlinActualAnnotationHasNoEffectInKotlinTypeChecker,
2727
FirProjectionRelationChecker,
28-
FirCommonAtomicReferenceToPrimitiveTypeChecker,
29-
FirCommonAtomicReferenceArrayToPrimitiveTypeChecker,
3028
FirArrayOfNothingTypeChecker,
3129
)
3230

compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
5656
import org.jetbrains.kotlin.util.ImplementationStatus
5757
import org.jetbrains.kotlin.util.OperatorNameConventions
5858
import org.jetbrains.kotlin.util.getChildren
59-
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
6059
import kotlin.contracts.ExperimentalContracts
6160
import kotlin.contracts.contract
6261

@@ -986,7 +985,7 @@ private fun ConeKotlinType.containsMalformedArgument(context: CheckerContext, al
986985
it.type?.fullyExpandedType(context.session)?.isMalformedExpandedType(context, allowNullableNothing) == true
987986
}
988987

989-
fun checkAtomicReferenceAccess(
988+
fun reportAtomicToPrimitiveProblematicAccess(
990989
type: ConeKotlinType,
991990
source: KtSourceElement?,
992991
atomicReferenceClassId: ClassId,
@@ -997,7 +996,7 @@ fun checkAtomicReferenceAccess(
997996
val expanded = type.fullyExpandedType(context.session)
998997
val argument = expanded.typeArguments.firstOrNull()?.type ?: return
999998

1000-
if (expanded.classId == atomicReferenceClassId && (argument.isPrimitive || argument.isValueClass(context.session))) {
999+
if (argument.isPrimitive || argument.isValueClass(context.session)) {
10011000
val candidate = appropriateCandidatesForArgument[argument.classId]
10021001
reporter.reportOn(source, FirErrors.ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY, atomicReferenceClassId, argument, candidate, context)
10031002
}

compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCommonAtomicReferenceToPrimitiveCallChecker.kt

+21-13
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,55 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
77

88
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
99
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
10-
import org.jetbrains.kotlin.fir.analysis.checkers.checkAtomicReferenceAccess
10+
import org.jetbrains.kotlin.fir.analysis.checkers.reportAtomicToPrimitiveProblematicAccess
1111
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
1212
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
1313
import org.jetbrains.kotlin.fir.references.resolved
14-
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
15-
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
14+
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
15+
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
16+
import org.jetbrains.kotlin.fir.types.classId
1617
import org.jetbrains.kotlin.fir.types.resolvedType
18+
import org.jetbrains.kotlin.name.CallableId
1719
import org.jetbrains.kotlin.name.ClassId
1820
import org.jetbrains.kotlin.name.StandardClassIds
21+
import org.jetbrains.kotlin.name.withClassId
1922

2023
abstract class AbstractAtomicReferenceToPrimitiveCallChecker(
21-
val atomicReferenceClassId: ClassId,
2224
val appropriateCandidatesForArgument: Map<ClassId, ClassId>,
2325
mppKind: MppCheckerKind,
26+
firstProblematicCallableId: CallableId,
27+
vararg remainingProblematicCallableIds: CallableId,
2428
) : FirFunctionCallChecker(mppKind) {
29+
val problematicCallableIds: Set<CallableId> = setOf(firstProblematicCallableId, *remainingProblematicCallableIds)
30+
2531
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
26-
val callable = expression.calleeReference.resolved?.resolvedSymbol ?: return
32+
val callable = expression.calleeReference.resolved?.resolvedSymbol as? FirFunctionSymbol<*> ?: return
33+
val receiverType = expression.dispatchReceiver?.resolvedType?.fullyExpandedType(context.session) ?: return
34+
val atomicReferenceClassId = receiverType.classId ?: return
35+
val fullyExpandedCallableId = callable.callableId.withClassId(atomicReferenceClassId)
2736

28-
if (callable.canInstantiateProblematicAtomicReference) {
29-
checkAtomicReferenceAccess(
30-
expression.resolvedType, expression.source,
37+
if (fullyExpandedCallableId in problematicCallableIds) {
38+
reportAtomicToPrimitiveProblematicAccess(
39+
receiverType, expression.source,
3140
atomicReferenceClassId, appropriateCandidatesForArgument,
3241
context, reporter
3342
)
3443
}
3544
}
36-
37-
open val FirBasedSymbol<*>.canInstantiateProblematicAtomicReference: Boolean
38-
get() = this is FirConstructorSymbol
3945
}
4046

4147
object FirCommonAtomicReferenceToPrimitiveCallChecker :
4248
AbstractAtomicReferenceToPrimitiveCallChecker(
43-
StandardClassIds.AtomicReference,
4449
StandardClassIds.atomicByPrimitive,
4550
MppCheckerKind.Platform,
51+
StandardClassIds.Callables.atomicReferenceCompareAndSet,
52+
StandardClassIds.Callables.atomicReferenceCompareAndExchange,
4653
)
4754

4855
object FirCommonAtomicArrayToPrimitiveCallChecker :
4956
AbstractAtomicReferenceToPrimitiveCallChecker(
50-
StandardClassIds.AtomicArray,
5157
StandardClassIds.atomicArrayByPrimitive,
5258
MppCheckerKind.Platform,
59+
StandardClassIds.Callables.atomicArrayCompareAndSetAt,
60+
StandardClassIds.Callables.atomicArrayCompareAndExchangeAt,
5361
)

compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/type/FirCommonAtomicReferenceToPrimitiveTypeChecker.kt

-42
This file was deleted.

compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1494,7 +1494,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
14941494
)
14951495
map.put(
14961496
ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY,
1497-
"''{0}'' uses identity equality, but ''{1}'' does not have a consistent identity.{2}",
1497+
"This call may have inconsistent results because ''{0}'' uses identity equality and ''{1}'' does not have a consistent identity.{2}",
14981498
CLASS_ID_RELATIVE_NAME_ONLY,
14991499
RENDER_TYPE,
15001500
suggestIfNotNull(" Consider using ''{0}'' instead.", CLASS_ID_RELATIVE_NAME_ONLY),

compiler/testData/diagnostics/nativeTests/errorProneAtomicArrayPrimitives.fir.kt

+8-8
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ import kotlin.concurrent.AtomicReference
1111
import kotlin.concurrent.AtomicArray
1212

1313
fun testKotlin() {
14-
val k = <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicArray(1) { 127 }<!>
15-
k.compareAndSet(0, 127, 128) // true
16-
k.compareAndSet(0, 128, 7777) // false
14+
val k = AtomicArray(1) { 127 }
15+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>k.compareAndSet(0, 127, 128)<!> // true
16+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>k.compareAndSet(0, 128, 7777)<!> // false
1717

18-
val kk: <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicArray<Int><!>
18+
val kk: AtomicArray<Int>
1919
kk = k
2020

21-
val l = <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicReference(127)<!>
22-
l.compareAndSet(127, 128) // true
23-
l.compareAndSet(128, 7777) // false
21+
val l = AtomicReference(127)
22+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>l.compareAndSet(127, 128)<!> // true
23+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>l.compareAndSet(128, 7777)<!> // false
2424

25-
val ll: <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicReference<Int><!>
25+
val ll: AtomicReference<Int>
2626
ll = l
2727
}

compiler/testData/diagnostics/tests/atomicReferenceToValueClass.fir.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ value class Box(val name: String)
1111
fun main() {
1212
val test = Box("Test")
1313
val rest = Box("Rest")
14-
val box = <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicReference(test)<!>
14+
val box = AtomicReference(test)
1515

16-
box.compareAndSet(test, rest)
16+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>box.compareAndSet(test, rest)<!>
1717
println(box.get())
1818
}

compiler/testData/diagnostics/tests/errorProneAtomicArrayPrimitives.fir.kt

+16-16
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,22 @@
88
import java.util.concurrent.atomic.AtomicReferenceArray
99

1010
fun testJava() {
11-
val j = <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicReferenceArray<Int>(127)<!>
12-
j.compareAndSet(0, 127, 128) // true
13-
j.compareAndSet(0, 128, 7777) // false
11+
val j = AtomicReferenceArray<Int>(127)
12+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>j.compareAndSet(0, 127, 128)<!> // true
13+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>j.compareAndSet(0, 128, 7777)<!> // false
1414

15-
val jj: <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicReferenceArray<Int><!>
15+
val jj: AtomicReferenceArray<Int>
1616
jj = j
1717
}
1818

1919
typealias JavaAtomicReferenceArray<T> = AtomicReferenceArray<T>
2020

2121
fun testTypealiasedJava() {
22-
val j = <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>JavaAtomicReferenceArray<Int>(127)<!>
23-
j.compareAndSet(0, 127, 128) // true
24-
j.compareAndSet(0, 128, 7777) // false
22+
val j = JavaAtomicReferenceArray<Int>(127)
23+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>j.compareAndSet(0, 127, 128)<!> // true
24+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>j.compareAndSet(0, 128, 7777)<!> // false
2525

26-
val jj: <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>JavaAtomicReferenceArray<Int><!>
26+
val jj: JavaAtomicReferenceArray<Int>
2727
jj = j
2828
}
2929

@@ -34,21 +34,21 @@ fun testTypealiasedJava() {
3434
import kotlin.concurrent.atomics.AtomicArray
3535

3636
fun testKotlin() {
37-
val k = <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicArray(arrayOf(127))<!>
38-
k.compareAndSetAt(0, 127, 128) // true
39-
k.compareAndSetAt(0, 128, 7777) // false
37+
val k = AtomicArray(arrayOf(127))
38+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>k.compareAndSetAt(0, 127, 128)<!> // true
39+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>k.compareAndSetAt(0, 128, 7777)<!> // false
4040

41-
val kk: <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>AtomicArray<Int><!>
41+
val kk: AtomicArray<Int>
4242
kk = k
4343
}
4444

4545
typealias KotlinAtomicArray<T> = AtomicArray<T>
4646

4747
fun testTypealiasedKotlin() {
48-
val k = <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>KotlinAtomicArray(arrayOf(127))<!>
49-
k.compareAndSetAt(0, 127, 128) // true
50-
k.compareAndSetAt(0, 128, 7777) // false
48+
val k = KotlinAtomicArray(arrayOf(127))
49+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>k.compareAndSetAt(0, 127, 128)<!> // true
50+
<!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>k.compareAndSetAt(0, 128, 7777)<!> // false
5151

52-
val kk: <!ATOMIC_REF_WITHOUT_CONSISTENT_IDENTITY!>KotlinAtomicArray<Int><!>
52+
val kk: KotlinAtomicArray<Int>
5353
kk = k
5454
}

0 commit comments

Comments
 (0)