Skip to content

Commit 7a34d38

Browse files
committed
Don't scream negative remainder errors and wrap overflow checks in one big try/catch
1 parent 3b86363 commit 7a34d38

File tree

2 files changed

+73
-46
lines changed

2 files changed

+73
-46
lines changed

compiler-plugin/runtime-components/src/main/kotlin/IntLongOverflowChecks.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ inline fun unaryMinus(a: Int): Int = Math.negateExact(a)
1111
inline fun abs(a: Int): Int = Math.absExact(a)
1212

1313
inline fun rem(a: Int, b: Int): Int {
14-
if (a < 0) {
15-
System.err.println("Warning: Remainder of a negative number")
14+
if (a < 0 && b != a && System.getProperty("aoc.warnOnNegativeRemainder") != "false") {
15+
System.err.println("Warning: Remainder of $a (a negative number)")
16+
System.setProperty("aoc.warnOnNegativeRemainder", "false")
1617
}
1718
return a % b
1819
}
@@ -29,8 +30,9 @@ inline fun unaryMinus(a: Long): Long = Math.negateExact(a)
2930
inline fun abs(a: Long): Long = Math.absExact(a)
3031

3132
inline fun rem(a: Long, b: Long): Long {
32-
if (a < 0) {
33-
System.err.println("Warning: Remainder of a negative number")
33+
if (a < 0 && b != a && System.getProperty("aoc.warnOnNegativeRemainder") != "false") {
34+
System.err.println("Warning: Remainder of $a (a negative number)")
35+
System.setProperty("aoc.warnOnNegativeRemainder", "false")
3436
}
3537
return a % b
3638
}

compiler-plugin/src/main/kotlin/OverflowChecker.kt

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ package com.sschr15.aoc.compiler.internal
33
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
44
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
55
import org.jetbrains.kotlin.builtins.PrimitiveType
6+
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
67
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
78
import org.jetbrains.kotlin.cli.jvm.compiler.report
89
import org.jetbrains.kotlin.config.CompilerConfiguration
910
import org.jetbrains.kotlin.ir.IrStatement
1011
import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
1112
import org.jetbrains.kotlin.ir.builders.irCall
12-
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
13-
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
14-
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
15-
import org.jetbrains.kotlin.ir.declarations.IrFunction
13+
import org.jetbrains.kotlin.ir.declarations.*
1614
import org.jetbrains.kotlin.ir.expressions.IrCall
1715
import org.jetbrains.kotlin.ir.expressions.IrExpression
1816
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
@@ -78,6 +76,7 @@ class OverflowChecker(private val context: IrPluginContext, private val config:
7876
}
7977

8078
lateinit var parent: IrDeclarationParent
79+
lateinit var file: IrFile
8180

8281
private fun IrType.isSumCandidate() =
8382
isSubtypeOfClass(context.irBuiltIns.iterableClass) || isArray() || isPrimitiveArray() || isSubtypeOfClass(sequenceClass)
@@ -145,6 +144,11 @@ class OverflowChecker(private val context: IrPluginContext, private val config:
145144
}
146145
}
147146

147+
override fun visitFile(declaration: IrFile): IrFile {
148+
file = declaration
149+
return super.visitFile(declaration)
150+
}
151+
148152
override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement {
149153
if (declaration.annotations.any { it.isAnnotationWithEqualFqName(FqName("com.sschr15.aoc.annotations.ExportIr")) }) {
150154
config.report(CompilerMessageSeverity.WARNING, declaration.dumpKotlinLike())
@@ -183,51 +187,72 @@ class OverflowChecker(private val context: IrPluginContext, private val config:
183187
}
184188

185189
override fun visitCall(expression: IrCall): IrExpression {
186-
if (!expression.type.isPrimitiveType()) return super.visitCall(expression)
190+
try {
191+
if (!expression.type.isPrimitiveType()) return super.visitCall(expression)
187192

188-
val extension = expression.extensionReceiver
189-
if (extension != null && extension.type.isSumCandidate()) {
190-
if (expression.symbol.owner.kotlinFqName in collectionChecks) {
191-
return visitSumCall(expression)
193+
val extension = expression.extensionReceiver
194+
if (extension != null && extension.type.isSumCandidate()) {
195+
if (expression.symbol.owner.kotlinFqName in collectionChecks) {
196+
return visitSumCall(expression)
197+
}
192198
}
193-
}
194199

195-
val par0 = expression.dispatchReceiver ?: expression.valueArguments.firstOrNull() ?: return super.visitCall(expression)
196-
if (par0.type.isPrimitiveType()) {
197-
val primitiveType = par0.type.getPrimitiveType()!!
198-
if (primitiveType == PrimitiveType.INT && expression.symbol.owner.name.asString() in intConversions) {
199-
return visitConversionCall(expression)
200-
} else if (primitiveType == PrimitiveType.LONG && expression.symbol.owner.name.asString() in longConversions) {
201-
return visitConversionCall(expression)
200+
val par0 = expression.dispatchReceiver ?: expression.valueArguments.firstOrNull() ?: return super.visitCall(
201+
expression
202+
)
203+
if (par0.type.isPrimitiveType()) {
204+
val primitiveType = par0.type.getPrimitiveType()!!
205+
if (primitiveType == PrimitiveType.INT && expression.symbol.owner.name.asString() in intConversions) {
206+
return visitConversionCall(expression)
207+
} else if (primitiveType == PrimitiveType.LONG && expression.symbol.owner.name.asString() in longConversions) {
208+
return visitConversionCall(expression)
209+
}
202210
}
203-
}
204-
205-
val primitiveType = expression.type.getPrimitiveType() ?: return super.visitCall(expression)
206-
if (primitiveType != PrimitiveType.INT && primitiveType != PrimitiveType.LONG) return super.visitCall(expression)
207-
if (expression.symbol.owner.name.asString() !in singleTypeChecks) return super.visitCall(expression)
208-
if (
209-
(expression.totalParameterCount != 2 || expression.getTotalParameter(0)!!.type != expression.getTotalParameter(1)!!.type) &&
210-
(expression.totalParameterCount != 1 || expression.symbol.owner.name.asString() !in singleArgumentTypeChecks)
211-
) {
212-
config.report(CompilerMessageSeverity.WARNING, "Unexpected number of arguments for ${expression.symbol.owner.name}, skipping")
213-
return super.visitCall(expression)
214-
}
215211

216-
return context.irBuiltIns.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset)
217-
.irCall(context.referenceFunctions(CallableId(
218-
FqName("com.sschr15.aoc.annotations"),
219-
null,
220-
expression.symbol.owner.name,
221-
)).single { it.owner.valueParameters.first().type == expression.type }).apply {
222-
expression.dispatchReceiver
223-
val offset = if (expression.dispatchReceiver != null) {
224-
putValueArgument(0, expression.dispatchReceiver!!)
212+
val primitiveType = expression.type.getPrimitiveType() ?: return super.visitCall(expression)
213+
if (primitiveType != PrimitiveType.INT && primitiveType != PrimitiveType.LONG) return super.visitCall(
214+
expression
215+
)
216+
if (expression.symbol.owner.name.asString() !in singleTypeChecks) return super.visitCall(expression)
217+
if (
218+
(expression.totalParameterCount != 2 || expression.getTotalParameter(0)!!.type != expression.getTotalParameter(
225219
1
226-
} else 0
220+
)!!.type) &&
221+
(expression.totalParameterCount != 1 || expression.symbol.owner.name.asString() !in singleArgumentTypeChecks)
222+
) {
223+
config.report(
224+
CompilerMessageSeverity.WARNING,
225+
"Unexpected number of arguments for ${expression.symbol.owner.name}, skipping"
226+
)
227+
return super.visitCall(expression)
228+
}
227229

228-
for (i in 0 until expression.valueArgumentsCount) {
229-
putValueArgument(i + offset, expression.getValueArgument(i))
230+
return context.irBuiltIns.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset)
231+
.irCall(
232+
context.referenceFunctions(
233+
CallableId(
234+
FqName("com.sschr15.aoc.annotations"),
235+
null,
236+
expression.symbol.owner.name,
237+
)
238+
).single { it.owner.valueParameters.first().type == expression.type }).apply {
239+
expression.dispatchReceiver
240+
val offset = if (expression.dispatchReceiver != null) {
241+
putValueArgument(0, expression.dispatchReceiver!!)
242+
1
243+
} else 0
244+
245+
for (i in 0 until expression.valueArgumentsCount) {
246+
putValueArgument(i + offset, expression.getValueArgument(i))
247+
}
230248
}
231-
}
249+
} catch (e: Exception) {
250+
config.report(
251+
CompilerMessageSeverity.ERROR,
252+
"Error while checking for overflow: ${e.stackTraceToString()}",
253+
CompilerMessageLocation.create(file.path, file.fileEntry.getLineNumber(expression.startOffset), file.fileEntry.getColumnNumber(expression.startOffset), null)
254+
)
255+
return expression
256+
}
232257
}
233258
}

0 commit comments

Comments
 (0)