mirror of
https://github.com/irmen/prog8.git
synced 2025-01-03 06:29:54 +00:00
stricter checks for negative array indexing
This commit is contained in:
parent
d18f2a7bfd
commit
2aae1f5e30
@ -1418,6 +1418,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(target is VarDecl) {
|
if(target is VarDecl) {
|
||||||
if(target.datatype !in IterableDatatypes && target.datatype!=DataType.UWORD)
|
if(target.datatype !in IterableDatatypes && target.datatype!=DataType.UWORD)
|
||||||
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
|
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
|
||||||
|
val indexVariable = arrayIndexedExpression.indexer.indexExpr as? IdentifierReference
|
||||||
|
if(indexVariable!=null) {
|
||||||
|
if(indexVariable.targetVarDecl(program)?.datatype in SignedDatatypes) {
|
||||||
|
errors.err("variable array indexing can't be performed with signed variables", indexVariable.position)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
val arraysize = target.arraysize?.constIndex()
|
val arraysize = target.arraysize?.constIndex()
|
||||||
val index = arrayIndexedExpression.indexer.constIndex()
|
val index = arrayIndexedExpression.indexer.constIndex()
|
||||||
if(arraysize!=null) {
|
if(arraysize!=null) {
|
||||||
|
@ -277,6 +277,10 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
val target = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
val target = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||||
val arraysize = target?.arraysize?.constIndex()
|
val arraysize = target?.arraysize?.constIndex()
|
||||||
if(arraysize!=null) {
|
if(arraysize!=null) {
|
||||||
|
if(arraysize+index < 0) {
|
||||||
|
errors.err("index out of bounds", arrayIndexedExpression.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
// replace the negative index by the normal index
|
// replace the negative index by the normal index
|
||||||
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
|
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
|
||||||
arrayIndexedExpression.indexer.indexExpr = newIndex
|
arrayIndexedExpression.indexer.indexExpr = newIndex
|
||||||
|
@ -535,7 +535,7 @@ main {
|
|||||||
byte @shared col = 20
|
byte @shared col = 20
|
||||||
col++
|
col++
|
||||||
ubyte @shared ubb = lsb(col as uword)
|
ubyte @shared ubb = lsb(col as uword)
|
||||||
uword @shared vaddr = bottom[col] as uword << 8 ; a mkword will get inserted here
|
uword @shared vaddr = bottom[cx16.r0L] as uword << 8 ; a mkword will get inserted here
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(VMTarget(), optimize=true, src, writeAssembly=false)!!
|
val result = compileText(VMTarget(), optimize=true, src, writeAssembly=false)!!
|
||||||
|
@ -6,6 +6,7 @@ import io.kotest.matchers.shouldNotBe
|
|||||||
import io.kotest.matchers.string.shouldContain
|
import io.kotest.matchers.string.shouldContain
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
import kotlin.io.path.readText
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
@ -220,5 +221,104 @@ main {
|
|||||||
}"""
|
}"""
|
||||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("negative array index variables are not allowed, but ptr indexing is allowed") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword @shared pointer
|
||||||
|
ubyte[10] array
|
||||||
|
str name = "hello"
|
||||||
|
|
||||||
|
byte sindex
|
||||||
|
|
||||||
|
array[sindex] = 10
|
||||||
|
cx16.r0L = array[sindex]
|
||||||
|
name[sindex] = 0
|
||||||
|
cx16.r0L = name[sindex]
|
||||||
|
pointer[sindex] = 10
|
||||||
|
cx16.r0L=pointer[sindex]
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(VMTarget(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 4
|
||||||
|
errors.errors[0] shouldContain "signed variables"
|
||||||
|
errors.errors[1] shouldContain "signed variables"
|
||||||
|
errors.errors[2] shouldContain "signed variables"
|
||||||
|
errors.errors[3] shouldContain "signed variables"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("bounds checking for both positive and negative indexes, correct cases") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte[10] array
|
||||||
|
array[0] = 0
|
||||||
|
array[9] = 0
|
||||||
|
array[-1] = 0
|
||||||
|
array[-10] = 0
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(VMTarget(), false, src, writeAssembly = false) shouldNotBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
test("bounds checking on strings, correct cases") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
str name = "1234567890"
|
||||||
|
name[0] = 0
|
||||||
|
name[9] = 0
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(VMTarget(), false, src, writeAssembly = false) shouldNotBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
test("bounds checking for positive indexes, invalid case") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte[10] array
|
||||||
|
array[10] = 0
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(VMTarget(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 1
|
||||||
|
errors.errors[0] shouldContain "out of bounds"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("bounds checking for negative indexes, invalid case") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte[10] array
|
||||||
|
array[-11] = 0
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(VMTarget(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 1
|
||||||
|
errors.errors[0] shouldContain "out of bounds"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("bounds checking on strings invalid cases") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
str name = "1234567890"
|
||||||
|
name[10] = 0
|
||||||
|
name[-1] = 0
|
||||||
|
name[-11] = 0
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(VMTarget(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 3
|
||||||
|
errors.errors[0] shouldContain "out of bounds"
|
||||||
|
errors.errors[1] shouldContain "out of bounds"
|
||||||
|
errors.errors[2] shouldContain "out of bounds"
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -337,7 +337,8 @@ An uword variable can be used in limited scenarios as a 'pointer' to a byte in m
|
|||||||
dynamic, location. You can use array indexing on a pointer variable to use it as a byte array at
|
dynamic, location. You can use array indexing on a pointer variable to use it as a byte array at
|
||||||
a dynamic location in memory: currently this is equivalent to directly referencing the bytes in
|
a dynamic location in memory: currently this is equivalent to directly referencing the bytes in
|
||||||
memory at the given index. In contrast to a real array variable, the index value can be the size of a word.
|
memory at the given index. In contrast to a real array variable, the index value can be the size of a word.
|
||||||
Unlike array variables, you cannot use a negative index to count from the end, because the size of the array is unknown.
|
Unlike array variables, negative indexing for pointer variables does *not* mean it will be counting from the end, because the size of the buffer is unknown.
|
||||||
|
Instead, it simply addresses memory that lies *before* the pointer variable.
|
||||||
See also :ref:`pointervars_programming`
|
See also :ref:`pointervars_programming`
|
||||||
|
|
||||||
**LSB/MSB split word arrays:**
|
**LSB/MSB split word arrays:**
|
||||||
|
@ -525,7 +525,8 @@ Array indexing
|
|||||||
|
|
||||||
Strings and arrays are a sequence of values. You can access the individual values by indexing.
|
Strings and arrays are a sequence of values. You can access the individual values by indexing.
|
||||||
Negative index means counted from the end of the array rather than the beginning, where -1 means
|
Negative index means counted from the end of the array rather than the beginning, where -1 means
|
||||||
the last element in the array, -2 the second-to-last, etc. (Python uses this same scheme)
|
the last element in the array, -2 the second-to-last, etc. (Python uses this same scheme.
|
||||||
|
Note that this syntax is only valid for arrays, not for strings! Python does allow the latter, but prog8 does not right now.)
|
||||||
Use brackets to index into an array: ``arrayvar[x]`` ::
|
Use brackets to index into an array: ``arrayvar[x]`` ::
|
||||||
|
|
||||||
array[2] ; the third byte in the array (index is 0-based)
|
array[2] ; the third byte in the array (index is 0-based)
|
||||||
|
@ -3,11 +3,6 @@ TODO
|
|||||||
|
|
||||||
See open issues on github.
|
See open issues on github.
|
||||||
|
|
||||||
Generate proper index out of bounds error for array[-11] if the array is only size 10. (only array[-1]..array[-10] are valid)
|
|
||||||
|
|
||||||
Putting a signed variable as an array index should be a compiler error. (using it for a pointer indexing is fine - but weird).
|
|
||||||
Add some more explanation about this to the "array indexing" paragraph in the docs.
|
|
||||||
|
|
||||||
Re-generate the skeletons doc files.
|
Re-generate the skeletons doc files.
|
||||||
|
|
||||||
optimize signed byte/word division by powers of 2 (and shift right?), it's now using divmod routine. (also % ?)
|
optimize signed byte/word division by powers of 2 (and shift right?), it's now using divmod routine. (also % ?)
|
||||||
|
@ -10,8 +10,10 @@ main {
|
|||||||
const ubyte NUMQUEENS=8
|
const ubyte NUMQUEENS=8
|
||||||
ubyte[NUMQUEENS] board
|
ubyte[NUMQUEENS] board
|
||||||
|
|
||||||
sub could_place(byte row, byte col) -> bool {
|
sub could_place(ubyte row, ubyte col) -> bool {
|
||||||
byte i
|
if row==0
|
||||||
|
return true
|
||||||
|
ubyte i
|
||||||
for i in 0 to row-1 {
|
for i in 0 to row-1 {
|
||||||
if board[i]==col or board[i]-i==col-row or board[i]+i==col+row
|
if board[i]==col or board[i]-i==col-row or board[i]+i==col+row
|
||||||
return false
|
return false
|
||||||
@ -44,7 +46,7 @@ main {
|
|||||||
}
|
}
|
||||||
ubyte col
|
ubyte col
|
||||||
for col in 0 to NUMQUEENS-1 {
|
for col in 0 to NUMQUEENS-1 {
|
||||||
if could_place(row as byte, col as byte) {
|
if could_place(row, col) {
|
||||||
board[row] = col
|
board[row] = col
|
||||||
; we need to save the local variables row and col.
|
; we need to save the local variables row and col.
|
||||||
sys.push(row)
|
sys.push(row)
|
||||||
|
@ -5,49 +5,9 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
uword @shared ptr = $2000
|
ubyte[10] array
|
||||||
uword @shared index = 1000
|
array[10] = 0
|
||||||
|
; array[-11] = 0
|
||||||
@($2000+1000) = 123
|
|
||||||
@($2000+1001) = 124
|
|
||||||
|
|
||||||
cx16.r0L = @(ptr+index)
|
|
||||||
cx16.r1L = @(ptr+1001)
|
|
||||||
|
|
||||||
txt.print_ub(cx16.r0L)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_ub(cx16.r1L)
|
|
||||||
txt.spc()
|
|
||||||
|
|
||||||
cx16.r2L = ptr[index]
|
|
||||||
cx16.r3L = ptr[1001]
|
|
||||||
|
|
||||||
txt.print_ub(cx16.r2L)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_ub(cx16.r3L)
|
|
||||||
txt.spc()
|
|
||||||
|
|
||||||
cx16.r0L = 200
|
|
||||||
cx16.r1L = 201
|
|
||||||
|
|
||||||
@(ptr+index) = cx16.r0L
|
|
||||||
@(ptr+1001) = cx16.r1L
|
|
||||||
|
|
||||||
txt.print_ub(@($2000+1000))
|
|
||||||
txt.spc()
|
|
||||||
txt.print_ub(@($2000+1001))
|
|
||||||
txt.spc()
|
|
||||||
|
|
||||||
cx16.r0L = 203
|
|
||||||
cx16.r1L = 204
|
|
||||||
|
|
||||||
ptr[index] = cx16.r0L
|
|
||||||
ptr[1001] = cx16.r1L
|
|
||||||
|
|
||||||
txt.print_ub(@($2000+1000))
|
|
||||||
txt.spc()
|
|
||||||
txt.print_ub(@($2000+1001))
|
|
||||||
txt.spc()
|
|
||||||
|
|
||||||
; txt.print_ub(ptr[index])
|
; txt.print_ub(ptr[index])
|
||||||
; txt.nl()
|
; txt.nl()
|
||||||
|
Loading…
Reference in New Issue
Block a user