Skip to content

Commit 80638eb

Browse files
committed
Prohibit unsupported suspend operators
contains/get/set operators don't work properly on both backends Also add box test checking that 'compareTo' operator works just fine #KT-16219 Fixed
1 parent 4921bd8 commit 80638eb

File tree

6 files changed

+114
-1
lines changed

6 files changed

+114
-1
lines changed

compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
8484
ReifiedTypeParameterAnnotationChecker(),
8585
DynamicReceiverChecker,
8686
DelegationChecker(),
87-
KClassWithIncorrectTypeArgumentChecker
87+
KClassWithIncorrectTypeArgumentChecker,
88+
SuspendOperatorsCheckers
8889
)
8990

9091
private val DEFAULT_CALL_CHECKERS = listOf(
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2010-2017 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.resolve.checkers
18+
19+
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
20+
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
21+
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
22+
import org.jetbrains.kotlin.diagnostics.Errors
23+
import org.jetbrains.kotlin.lexer.KtTokens
24+
import org.jetbrains.kotlin.psi.KtDeclaration
25+
import org.jetbrains.kotlin.resolve.BindingContext
26+
import org.jetbrains.kotlin.util.OperatorNameConventions
27+
28+
object SuspendOperatorsCheckers : SimpleDeclarationChecker {
29+
private val UNSUPPORTED_OPERATOR_NAMES = setOf(
30+
OperatorNameConventions.CONTAINS,
31+
OperatorNameConventions.GET, OperatorNameConventions.SET
32+
)
33+
34+
override fun check(
35+
declaration: KtDeclaration,
36+
descriptor: DeclarationDescriptor,
37+
diagnosticHolder: DiagnosticSink,
38+
bindingContext: BindingContext
39+
) {
40+
if (descriptor is FunctionDescriptor && descriptor.isSuspend && descriptor.isOperator &&
41+
descriptor.name in UNSUPPORTED_OPERATOR_NAMES) {
42+
declaration.modifierList?.getModifier(KtTokens.OPERATOR_KEYWORD)?.let {
43+
diagnosticHolder.report(Errors.UNSUPPORTED.on(it, "suspend operator \"${descriptor.name}\""))
44+
}
45+
}
46+
}
47+
}

compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/operators.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ class A(val x: String) {
4444
x.resume(Unit)
4545
COROUTINE_SUSPENDED
4646
}
47+
// See KT-16221
48+
// operator suspend fun contains(y: String): Boolean = suspendCoroutineOrReturn { x ->
49+
// x.resume(y == "56")
50+
// COROUTINE_SUSPENDED
51+
// }
52+
53+
operator suspend fun compareTo(y: String): Int = suspendCoroutineOrReturn { x ->
54+
x.resume("56".compareTo(y))
55+
COROUTINE_SUSPENDED
56+
}
4757
}
4858

4959
fun builder(c: suspend () -> Unit) {
@@ -94,6 +104,22 @@ suspend fun foo7() {
94104
if (!y.isIncCalled) throw RuntimeException("fail 8")
95105
}
96106

107+
//suspend fun foo8() {
108+
// if ("1" in a) throw RuntimeException("fail 9")
109+
// if (!("1" !in a)) throw RuntimeException("fail 9")
110+
//
111+
// if ("56" in a) throw RuntimeException("fail 10")
112+
// if (!("56" !in a)) throw RuntimeException("fail 11")
113+
//}
114+
115+
suspend fun checkCompareTo(v: String) = (a < v) == ("56" < v)
116+
117+
suspend fun foo9() {
118+
if (!checkCompareTo("55")) throw RuntimeException("fail 12")
119+
if (!checkCompareTo("56")) throw RuntimeException("fail 13")
120+
if (!checkCompareTo("57")) throw RuntimeException("fail 14")
121+
}
122+
97123
fun box(): String {
98124

99125
builder {
@@ -104,6 +130,8 @@ fun box(): String {
104130
//foo5()
105131
foo6()
106132
foo7()
133+
//foo8()
134+
foo9()
107135
}
108136

109137
return "OK"

compiler/testData/codegen/light-analysis/coroutines/suspendFunctionAsCoroutine/operators.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public final class A {
66
private field isSetValueCalled: boolean
77
private final @org.jetbrains.annotations.NotNull field x: java.lang.String
88
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
9+
public final @org.jetbrains.annotations.Nullable method compareTo(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
910
public final @org.jetbrains.annotations.Nullable method component1(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
1011
public final @org.jetbrains.annotations.Nullable method getValue(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.reflect.KProperty, @org.jetbrains.annotations.NotNull p2: kotlin.coroutines.experimental.Continuation): java.lang.Object
1112
public final @org.jetbrains.annotations.NotNull method getX(): java.lang.String
@@ -55,12 +56,14 @@ public final class OperatorsKt {
5556
private static @org.jetbrains.annotations.NotNull field a: A
5657
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
5758
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
59+
public final static @org.jetbrains.annotations.Nullable method checkCompareTo(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
5860
public final static @org.jetbrains.annotations.Nullable method foo1(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
5961
public final static @org.jetbrains.annotations.Nullable method foo2(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
6062
public final static @org.jetbrains.annotations.Nullable method foo3(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
6163
public final static @org.jetbrains.annotations.Nullable method foo4(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
6264
public final static @org.jetbrains.annotations.Nullable method foo6(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
6365
public final static @org.jetbrains.annotations.Nullable method foo7(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
66+
public final static @org.jetbrains.annotations.Nullable method foo9(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
6467
public final static @org.jetbrains.annotations.NotNull method getA(): A
6568
public final static method setA(@org.jetbrains.annotations.NotNull p0: A): void
6669
public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// !DIAGNOSTICS: -UNUSED_PARAMETER
2+
// SKIP_TXT
3+
4+
class A {
5+
suspend <!UNSUPPORTED!>operator<!> fun get(x: Int) = 1
6+
suspend <!UNSUPPORTED!>operator<!> fun set(x: Int, v: String) {}
7+
8+
<!UNSUPPORTED!>operator<!> suspend fun contains(y: String): Boolean = true
9+
}
10+
11+
class B
12+
suspend <!UNSUPPORTED!>operator<!> fun B.get(x: Int) =1
13+
suspend <!UNSUPPORTED!>operator<!> fun B.set(x: Int, v: String) {}
14+
15+
<!UNSUPPORTED!>operator<!> suspend fun B.contains(y: String): Boolean = true
16+
17+
class C {
18+
suspend fun get(x: Int) = 1
19+
suspend fun set(x: Int, v: String) {}
20+
21+
suspend fun contains(y: String): Boolean = true
22+
}
23+
24+
class D
25+
suspend fun D.get(x: Int) =1
26+
suspend fun D.set(x: Int, v: String) {}
27+
28+
suspend fun D.contains(y: String): Boolean = true

compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,12 @@ public void testNonLocalSuspension() throws Exception {
773773
doTest(fileName);
774774
}
775775

776+
@TestMetadata("operators.kt")
777+
public void testOperators() throws Exception {
778+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/operators.kt");
779+
doTest(fileName);
780+
}
781+
776782
@TestMetadata("suspendApplicability.kt")
777783
public void testSuspendApplicability() throws Exception {
778784
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendApplicability.kt");

0 commit comments

Comments
 (0)