diff --git a/compiler/res/version.txt b/compiler/res/version.txt index 37722ebbc..815da58b7 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -7.4 +7.4.1 diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 9dc7134f2..abc7fa8e0 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -257,7 +257,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions // perform initial syntax checks and processings println("Processing for target ${compilerOptions.compTarget.name}...") program.preprocessAst(program) - program.checkIdentifiers(errors, compilerOptions) + program.checkIdentifiers(errors, program, compilerOptions) errors.report() // TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR // ...but what do we gain from this? We can leave it as it is now: where a char literal is no more than syntactic sugar for an UBYTE value. @@ -274,7 +274,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions errors.report() program.checkValid(errors, compilerOptions) errors.report() - program.checkIdentifiers(errors, compilerOptions) + program.checkIdentifiers(errors, program, compilerOptions) errors.report() } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 1977e794e..d341c8a11 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1015,8 +1015,7 @@ internal class AstChecker(private val program: Program, } - val error = - VerifyFunctionArgTypes.checkTypes(functionCallStatement, program) + val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, program) if(error!=null) { errors.err(error, functionCallStatement.args.firstOrNull()?.position ?: functionCallStatement.position) } diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 12c9fd33f..239c00cae 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -73,9 +73,9 @@ internal fun Program.preprocessAst(program: Program) { mods = transforms.applyModifications() } -internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) { +internal fun Program.checkIdentifiers(errors: IErrorReporter, program: Program, options: CompilationOptions) { - val checker2 = AstIdentifiersChecker(errors, options.compTarget) + val checker2 = AstIdentifiersChecker(errors, program, options.compTarget) checker2.visit(this) if(errors.noErrors()) { diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index 7ed5fc743..7bc38f6c1 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -1,6 +1,11 @@ package prog8.compiler.astprocessing +import prog8.ast.IFunctionCall +import prog8.ast.Node +import prog8.ast.Program +import prog8.ast.base.FatalAstException import prog8.ast.base.Position +import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.StringLiteralValue import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor @@ -8,7 +13,9 @@ import prog8.compilerinterface.BuiltinFunctions import prog8.compilerinterface.ICompilationTarget import prog8.compilerinterface.IErrorReporter -internal class AstIdentifiersChecker(private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor { +internal class AstIdentifiersChecker(private val errors: IErrorReporter, + private val program: Program, + private val compTarget: ICompilationTarget) : IAstVisitor { private var blocks = mutableMapOf() private fun nameError(name: String, position: Position, existing: Statement) { @@ -121,4 +128,27 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter, private super.visit(string) } + + override fun visit(functionCall: FunctionCall) = visitFunctionCall(functionCall) + override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement) + + private fun visitFunctionCall(call: IFunctionCall) { + when (val target = call.target.targetStatement(program)) { + is Subroutine -> { + if(call.args.size != target.parameters.size) + errors.err("invalid number of arguments", call.args[0].position) + } + is BuiltinFunctionStatementPlaceholder -> { + val func = BuiltinFunctions.getValue(target.name) + if(call.args.size != func.parameters.size) + errors.err("invalid number of arguments", call.args[0].position) + } + is Label -> { + if(call.args.isNotEmpty()) + errors.err("cannot use arguments when calling a label", call.args[0].position) + } + null -> {} + else -> throw FatalAstException("weird call target") + } + } } diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index 46838ce87..4e473ace2 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -11,7 +11,7 @@ import prog8.ast.walk.IAstVisitor import prog8.compilerinterface.BuiltinFunctions import prog8.compilerinterface.InternalCompilerException -class VerifyFunctionArgTypes(val program: Program) : IAstVisitor { +internal class VerifyFunctionArgTypes(val program: Program) : IAstVisitor { override fun visit(functionCall: FunctionCall) { val error = checkTypes(functionCall as IFunctionCall, program) diff --git a/compiler/test/TestSubroutines.kt b/compiler/test/TestSubroutines.kt index 53a53e00f..19967f565 100644 --- a/compiler/test/TestSubroutines.kt +++ b/compiler/test/TestSubroutines.kt @@ -9,6 +9,7 @@ import io.kotest.matchers.types.instanceOf import prog8.ast.base.DataType import prog8.ast.expressions.* import prog8.ast.statements.* +import prog8.compiler.printProgram import prog8.compiler.target.C64Target import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.assertFailure @@ -262,4 +263,66 @@ class TestSubroutines: FunSpec({ addressExpr.operator shouldBe "+" (addressExpr.right as NumericLiteralValue).number.toInt() shouldBe 10 } + + test("invalid number of args check on normal subroutine") { + val text=""" + main { + sub thing(ubyte a1, ubyte a2) { + } + + sub start() { + thing(1) + thing(1,2) + thing(1,2,3) + } + } + """ + + val errors = ErrorReporterForTests() + compileText(C64Target, false, text, writeAssembly = false, errors=errors).assertFailure() + errors.errors.size shouldBe 2 + errors.errors[0] shouldContain "7:24: invalid number of arguments" + errors.errors[1] shouldContain "9:24: invalid number of arguments" + } + + test("invalid number of args check on asm subroutine") { + val text=""" + main { + asmsub thing(ubyte a1 @A, ubyte a2 @Y) { + } + + sub start() { + thing(1) + thing(1,2) + thing(1,2,3) + } + } + """ + + val errors = ErrorReporterForTests() + compileText(C64Target, false, text, writeAssembly = false, errors=errors).assertFailure() + errors.errors.size shouldBe 2 + errors.errors[0] shouldContain "7:24: invalid number of arguments" + errors.errors[1] shouldContain "9:24: invalid number of arguments" + } + + test("invalid number of args check on call to label and builtin func") { + val text=""" + main { + label: + sub start() { + label() + label(1) + void rnd() + void rnd(1) + } + } + """ + + val errors = ErrorReporterForTests() + compileText(C64Target, false, text, writeAssembly = false, errors=errors).assertFailure() + errors.errors.size shouldBe 2 + errors.errors[0] shouldContain "cannot use arguments" + errors.errors[1] shouldContain "invalid number of arguments" + } }) diff --git a/examples/test.p8 b/examples/test.p8 index e729ca787..529d3e079 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -10,7 +10,14 @@ main { byte b2 = 22 word b3 = 3333 dummy++ - func(-b1,-b2,-b3) + labelz() + labelz(1) + printz(1) + printz(1,2,3,4,5,6) + func(-b1,-b2,-b3 , 3, 4, 5) + +labelz: + } sub printz(word a1, byte a2, word a3) {