From 4c080afb761229e07e5feab53d1baa5f0db1049b Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 21 Apr 2021 22:45:03 +0200 Subject: [PATCH] added compiler check against impossible for loop range (unsigned downto exactly 0 with non-const startvalue and step != -1) --- .../compiler/astprocessing/AstChecker.kt | 17 ++++++ .../target/cpu6502/codegen/ForLoopsAsmGen.kt | 7 +++ examples/test.p8 | 58 ++++++++++++++++--- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index d7f97a0c5..8a5cf2be7 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -92,6 +92,18 @@ internal class AstChecker(private val program: Program, } override fun visit(forLoop: ForLoop) { + + fun checkUnsignedLoopDownto0(range: RangeExpr?) { + if(range==null) + return + val step = range.step.constValue(program)?.number?.toDouble() ?: 1.0 + if(step < -1.0) { + val limit = range.to.constValue(program)?.number?.toDouble() + if(limit==0.0 && range.from.constValue(program)==null) + errors.err("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping", forLoop.position) + } + } + val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE) if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) { errors.err("can only loop over an iterable type", forLoop.position) @@ -104,11 +116,15 @@ internal class AstChecker(private val program: Program, DataType.UBYTE -> { if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR) errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position) + + checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpr) } DataType.UWORD -> { if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR && iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW) errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position) + + checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpr) } DataType.BYTE -> { if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B) @@ -146,6 +162,7 @@ internal class AstChecker(private val program: Program, super.visit(forLoop) } + override fun visit(jump: Jump) { val ident = jump.identifier if(ident!=null) { diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/ForLoopsAsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/ForLoopsAsmGen.kt index eeaba7490..425200e29 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/ForLoopsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/ForLoopsAsmGen.kt @@ -40,6 +40,13 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen: val modifiedLabel2 = asmgen.makeLabel("for_modifiedb") asmgen.loopEndLabels.push(endLabel) val stepsize=range.step.constValue(program)!!.number.toInt() + + if(stepsize < -1) { + val limit = range.to.constValue(program)?.number?.toDouble() + if(limit==0.0) + throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping") + } + when(iterableDt) { DataType.ARRAY_B, DataType.ARRAY_UB -> { if (stepsize==1 || stepsize==-1) { diff --git a/examples/test.p8 b/examples/test.p8 index 5a5a8a92f..ad1ee25a6 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,21 +7,61 @@ main { sub start() { - ubyte num_entries + ubyte fromub = 120 + uword fromuw = 400 + byte fromb = 120 + word fromw = 400 - num_entries = 10 - ubyte ix - for ix in (num_entries-1)*2 downto 0 step -2 { - txt.print_ub(ix) + uword counter + byte bc + ubyte ubc + word wc + uword uwc + + counter = 0 + for bc in fromb to 0 step -1 { + counter++ + txt.print_b(bc) txt.spc() } - txt.nl() - num_entries = 10 - for ix in num_entries-1 downto 0 { - txt.print_ub(ix*2) + txt.print_uw(counter) + txt.nl() + txt.nl() + + counter = 0 + for ubc in fromub to 0 step -1 { + counter++ + txt.print_ub(ubc) txt.spc() } + txt.nl() + txt.print_uw(counter) + txt.nl() + txt.nl() + + counter = 0 + for wc in fromw to 0 step -1 { + counter++ + txt.print_w(wc) + txt.spc() + } + txt.nl() + txt.print_uw(counter) + txt.nl() + txt.nl() + + ; TODO FIX THIS LOOP?? + counter = 0 + for uwc in fromuw to 0 step -1 { + counter++ + txt.print_uw(uwc) + txt.spc() + } + txt.nl() + txt.print_uw(counter) + txt.nl() + txt.nl() } }