diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index db3a97cdc23..99eb2393744 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -17486,6 +17486,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/integerLiterals"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("constantUnaryOperators.kt") + public void testConstantUnaryOperators() throws Exception { + runTest("compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.kt"); + } + @Test @TestMetadata("intToLongConversion.kt") public void testIntToLongConversion() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index b2f1bf1ec7a..0e95e8502d6 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -17486,6 +17486,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/integerLiterals"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("constantUnaryOperators.kt") + public void testConstantUnaryOperators() throws Exception { + runTest("compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.kt"); + } + @Test @TestMetadata("intToLongConversion.kt") public void testIntToLongConversion() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index ba65bc8c193..26df67d766d 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -17486,6 +17486,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/integerLiterals"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("constantUnaryOperators.kt") + public void testConstantUnaryOperators() throws Exception { + runTest("compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.kt"); + } + @Test @TestMetadata("intToLongConversion.kt") public void testIntToLongConversion() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/ConvertibleIntegerOperators.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/ConvertibleIntegerOperators.kt index 7ed292307b5..6a47855f350 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/ConvertibleIntegerOperators.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/ConvertibleIntegerOperators.kt @@ -5,15 +5,19 @@ package org.jetbrains.kotlin.fir.scopes.impl -import org.jetbrains.kotlin.name.CallableId -import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.name.StandardClassIds object ConvertibleIntegerOperators { - val operatorsNames: Set = listOf( + val binaryOperatorsNames: Set = listOf( "plus", "minus", "times", "div", "rem", "and", "or", "xor", "shl", "shr", "ushr" - ).mapTo(mutableSetOf()) { Name.identifier(it) } + ).toNameSet() + + // Constant conversion for those unary operators works only for signed integers + val unaryOperatorNames: Set = listOf( + "inv", "unaryPlus", "unaryMinus" + ).toNameSet() + + private fun List.toNameSet(): Set = mapTo(mutableSetOf()) { Name.identifier(it) } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirIntegerConstantOperatorScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirIntegerConstantOperatorScope.kt index c617b0a70fd..22741b7df7a 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirIntegerConstantOperatorScope.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirIntegerConstantOperatorScope.kt @@ -40,12 +40,18 @@ class FirIntegerConstantOperatorScope( private val mappedFunctions = mutableMapOf() override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) { - if (name !in ConvertibleIntegerOperators.operatorsNames) { + // Constant conversion for those unary operators works only for signed integers + val isUnaryOperator = !isUnsigned && (name in ConvertibleIntegerOperators.unaryOperatorNames) + val isBinaryOperator = name in ConvertibleIntegerOperators.binaryOperatorsNames + if (!isUnaryOperator && !isBinaryOperator) { return baseScope.processFunctionsByName(name, processor) } val wrappedSymbol = mappedFunctions.getOrPut(name) { val allFunctions = baseScope.getFunctions(name) val functionSymbol = allFunctions.first { + // unary operators have only one overload + if (isUnaryOperator) return@first true + val coneType = it.fir.valueParameters.first().returnTypeRef.coneType if (isUnsigned) { coneType.isUInt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt index a194185ce73..ddacd4ec672 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt @@ -94,6 +94,8 @@ class CandidateFactory private constructor( private fun FirBasedSymbol<*>.unwrapIntegerOperatorSymbolIfNeeded(callInfo: CallInfo): FirBasedSymbol<*> { if (this !is FirNamedFunctionSymbol) return this + // There is no need to unwrap unary operators + if (fir.valueParameters.isEmpty()) return this val original = fir.originalForWrappedIntegerOperator ?: return this return if (callInfo.arguments.first().isIntegerLiteralOrOperatorCall()) { this diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index aa2ec01c7bd..07cb98de1fc 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -395,8 +395,13 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform } ?: return this if (!resolvedSymbol.isWrappedIntegerOperator()) return this - val argument = this.argumentList.arguments.singleOrNull() ?: return this - assert(argument.isIntegerLiteralOrOperatorCall()) + val arguments = this.argumentList.arguments + val argument = when (arguments.size) { + 0 -> null + 1 -> arguments.first() + else -> return this + } + assert(argument?.isIntegerLiteralOrOperatorCall() != false) val originalCall = this diff --git a/compiler/testData/codegen/box/unaryOp/longOverflow.kt b/compiler/testData/codegen/box/unaryOp/longOverflow.kt index 4d49b743125..4dc123b9d9e 100644 --- a/compiler/testData/codegen/box/unaryOp/longOverflow.kt +++ b/compiler/testData/codegen/box/unaryOp/longOverflow.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR -// FIR status: don't support legacy feature; for reasons this test is ignored, go to KT-46419 - fun box(): String { val a: Long = -(1 shl 31) if (a != -2147483648L) return "fail: in this case we should add to ints and than cast the result to long - overflow expected" diff --git a/compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.fir.txt b/compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.fir.txt new file mode 100644 index 00000000000..e2e0953ced0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.fir.txt @@ -0,0 +1,31 @@ +FILE: constantUnaryOperators.kt + public final val i1: R|kotlin/Int| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).R|kotlin/Int.inv|() + public get(): R|kotlin/Int| + public final val l1: R|kotlin/Long| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).R|kotlin/Int.inv|().R|kotlin/Int.toLong|() + public get(): R|kotlin/Long| + public final val ll1: R|kotlin/Long| = Long(3000000000).R|kotlin/Long.times|(Int(2)).R|kotlin/Long.plus|(Int(1)).R|kotlin/Long.inv|() + public get(): R|kotlin/Long| + public final val i2: R|kotlin/Int| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).R|kotlin/Int.unaryPlus|() + public get(): R|kotlin/Int| + public final val l2: R|kotlin/Long| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).R|kotlin/Int.unaryPlus|().R|kotlin/Int.toLong|() + public get(): R|kotlin/Long| + public final val ll2: R|kotlin/Long| = Long(3000000000).R|kotlin/Long.times|(Int(2)).R|kotlin/Long.plus|(Int(1)).R|kotlin/Long.unaryPlus|() + public get(): R|kotlin/Long| + public final val i3: R|kotlin/Int| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).R|kotlin/Int.unaryMinus|() + public get(): R|kotlin/Int| + public final val l3: R|kotlin/Long| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).R|kotlin/Int.unaryMinus|().R|kotlin/Int.toLong|() + public get(): R|kotlin/Long| + public final val ll3: R|kotlin/Long| = Long(3000000000).R|kotlin/Long.times|(Int(2)).R|kotlin/Long.plus|(Int(1)).R|kotlin/Long.unaryMinus|() + public get(): R|kotlin/Long| + public final val i4: R|kotlin/Int| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).R|kotlin/Int.inc|() + public get(): R|kotlin/Int| + public final val l4: R|kotlin/Long| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).#() + public get(): R|kotlin/Long| + public final val ll4: R|kotlin/Long| = Long(3000000000).R|kotlin/Long.times|(Int(2)).R|kotlin/Long.plus|(Int(1)).R|kotlin/Long.inc|() + public get(): R|kotlin/Long| + public final val i5: R|kotlin/Int| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).R|kotlin/Int.dec|() + public get(): R|kotlin/Int| + public final val l5: R|kotlin/Long| = Int(2).R|kotlin/Int.plus|(Int(2).R|kotlin/Int.times|(Int(3))).#() + public get(): R|kotlin/Long| + public final val ll5: R|kotlin/Long| = Long(3000000000).R|kotlin/Long.times|(Int(2)).R|kotlin/Long.plus|(Int(1)).R|kotlin/Long.dec|() + public get(): R|kotlin/Long| diff --git a/compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.kt b/compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.kt new file mode 100644 index 00000000000..62b6935b4c8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.kt @@ -0,0 +1,27 @@ +// FIR_IDENTICAL +// SKIP_TXT +// FIR_DUMP + +// ------------- const ------------- + +val i1 = (2 + 2 * 3).inv() +val l1: Long = (2 + 2 * 3).inv() +val ll1 = (3000000000 * 2 + 1).inv() + +val i2 = (2 + 2 * 3).unaryPlus() +val l2: Long = (2 + 2 * 3).unaryPlus() +val ll2 = (3000000000 * 2 + 1).unaryPlus() + +val i3 = (2 + 2 * 3).unaryMinus() +val l3: Long = (2 + 2 * 3).unaryMinus() +val ll3 = (3000000000 * 2 + 1).unaryMinus() + +// ------------- non const ------------- + +val i4 = (2 + 2 * 3).inc() +val l4: Long = (2 + 2 * 3).inc() +val ll4 = (3000000000 * 2 + 1).inc() + +val i5 = (2 + 2 * 3).dec() +val l5: Long = (2 + 2 * 3).dec() +val ll5 = (3000000000 * 2 + 1).dec() diff --git a/compiler/testData/diagnostics/tests/numbers/literalReceiverWithIntegerValueType.fir.kt b/compiler/testData/diagnostics/tests/numbers/literalReceiverWithIntegerValueType.fir.kt index cde436975cb..2c47902bb54 100644 --- a/compiler/testData/diagnostics/tests/numbers/literalReceiverWithIntegerValueType.fir.kt +++ b/compiler/testData/diagnostics/tests/numbers/literalReceiverWithIntegerValueType.fir.kt @@ -14,15 +14,15 @@ fun testLongDotCall(c1: C) { c1.takeT(1.rem(2)) c1.takeT(1.inc()) c1.takeT(1.dec()) - c1.takeT(1.unaryPlus()) - c1.takeT(1.unaryMinus()) + c1.takeT(1.unaryPlus()) + c1.takeT(1.unaryMinus()) c1.takeT(1.shl(2)) c1.takeT(1.shr(2)) c1.takeT(1.ushr(2)) c1.takeT(1.and(2)) c1.takeT(1.or(2)) c1.takeT(1.xor(2)) - c1.takeT(1.inv()) + c1.takeT(1.inv()) } fun testShortDotCall(c2: C) { diff --git a/compiler/testData/diagnostics/tests/numbers/newLiteralOperatorsResolution_newResolve.fir.kt b/compiler/testData/diagnostics/tests/numbers/newLiteralOperatorsResolution_newResolve.fir.kt index 9e1a41212ec..e93e2602263 100644 --- a/compiler/testData/diagnostics/tests/numbers/newLiteralOperatorsResolution_newResolve.fir.kt +++ b/compiler/testData/diagnostics/tests/numbers/newLiteralOperatorsResolution_newResolve.fir.kt @@ -70,9 +70,9 @@ fun testLongUnaryOperators() { takeLong(-1) // Mismatch - takeLong(2.unaryPlus()) - takeLong(2.unaryMinus()) - takeLong(2.inv()) + takeLong(2.unaryPlus()) + takeLong(2.unaryMinus()) + takeLong(2.inv()) takeLong(1.inc()) takeLong(1.dec()) } diff --git a/compiler/testData/diagnostics/tests/numbers/newLiteralOperatorsResolution_warning.fir.kt b/compiler/testData/diagnostics/tests/numbers/newLiteralOperatorsResolution_warning.fir.kt index 44a34269510..2c8aeda5d04 100644 --- a/compiler/testData/diagnostics/tests/numbers/newLiteralOperatorsResolution_warning.fir.kt +++ b/compiler/testData/diagnostics/tests/numbers/newLiteralOperatorsResolution_warning.fir.kt @@ -68,9 +68,9 @@ fun testLongUnaryOperators() { // Won't change takeLong(+1) takeLong(-1) - takeLong(2.unaryPlus()) - takeLong(2.unaryMinus()) - takeLong(2.inv()) + takeLong(2.unaryPlus()) + takeLong(2.unaryMinus()) + takeLong(2.inv()) // Will change takeLong(1.inc()) diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index e23b7fc69ab..3530ae808f6 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -17492,6 +17492,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/integerLiterals"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("constantUnaryOperators.kt") + public void testConstantUnaryOperators() throws Exception { + runTest("compiler/testData/diagnostics/tests/integerLiterals/constantUnaryOperators.kt"); + } + @Test @TestMetadata("intToLongConversion.kt") public void testIntToLongConversion() throws Exception {