fix compiler crash when initializing an array var with another array var

This commit is contained in:
Irmen de Jong 2022-01-23 14:23:34 +01:00
parent 4bf4771f08
commit 5766208207
5 changed files with 94 additions and 49 deletions

View File

@ -571,8 +571,11 @@ class AsmGen(private val program: Program,
private fun makeArrayFillDataSigned(decl: VarDecl): List<String> {
val array =
if(decl.value!=null)
if(decl.value!=null) {
if(decl.value !is ArrayLiteralValue)
throw AssemblyError("can only use array literal values as array initializer value")
(decl.value as ArrayLiteralValue).value
}
else {
// no array init value specified, use a list of zeros
val zero = decl.zeroElementValue()

View File

@ -44,45 +44,65 @@ internal class StatementReorderer(val program: Program,
private val declsProcessedWithInitAssignment = mutableSetOf<VarDecl>()
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) {
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
// A numeric vardecl without an initial value is initialized with zero,
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
// This allows you to restart the program and have the same starting values of the variables
// So basically consider 'ubyte xx' as a short form for 'ubyte xx; xx=0'
if (decl.type == VarDeclType.VAR) {
if (decl.datatype in NumericDatatypes) {
if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) {
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
// A numeric vardecl without an initial value is initialized with zero,
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
// This allows you to restart the program and have the same starting values of the variables
// So basically consider 'ubyte xx' as a short form for 'ubyte xx; xx=0'
decl.value = null
if(decl.name.startsWith("retval_interm_") && decl.definingScope.name=="prog8_lib") {
// no need to zero out the special internal returnvalue intermediates.
return noModifications
}
if(decl.findInitializer(program)!=null)
return noModifications // an initializer assignment for a vardecl is already here
val nextFor = decl.nextSibling() as? ForLoop
val hasNextForWithThisLoopvar = nextFor?.loopVar?.nameInSource==listOf(decl.name)
if (!hasNextForWithThisLoopvar) {
// Add assignment to initialize with zero
// Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later.
val identifier = IdentifierReference(listOf(decl.name), decl.position)
val assignzero = Assignment(AssignTarget(identifier, null, null, decl.position), decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
return listOf(IAstModification.InsertAfter(
decl, assignzero, parent as IStatementContainer
))
}
}
} else {
// Transform the vardecl with initvalue to a plain vardecl + assignment
// this allows for other optimizations to kick in.
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
val assign = Assignment(AssignTarget(identifier, null, null, pos), decl.value!!, AssignmentOrigin.VARINIT, pos)
decl.value = null
if(decl.name.startsWith("retval_interm_") && decl.definingScope.name=="prog8_lib") {
// no need to zero out the special internal returnvalue intermediates.
return noModifications
}
if(decl.findInitializer(program)!=null)
return noModifications // an initializer assignment for a vardecl is already here
val nextFor = decl.nextSibling() as? ForLoop
val hasNextForWithThisLoopvar = nextFor?.loopVar?.nameInSource==listOf(decl.name)
if (!hasNextForWithThisLoopvar) {
// Add assignment to initialize with zero
// Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later.
val identifier = IdentifierReference(listOf(decl.name), decl.position)
val assignzero = Assignment(AssignTarget(identifier, null, null, decl.position), decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
return listOf(IAstModification.InsertAfter(
decl, assignzero, parent as IStatementContainer
))
}
return listOf(IAstModification.InsertAfter(
decl, assign, parent as IStatementContainer
))
}
}
}
else if(decl.datatype in ArrayDatatypes) {
// only if the initializer expression is a reference to another array, split it into a separate assignment.
// this is so that it later can be changed into a memcopy.
// (that code only triggers on regular assignment, not on variable initializers)
val ident = decl.value as? IdentifierReference
if(ident!=null) {
val target = ident.targetVarDecl(program)
if(target!=null && target.isArray) {
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
val assign = Assignment(AssignTarget(identifier, null, null, pos), decl.value!!, AssignmentOrigin.VARINIT, pos)
decl.value = null
return listOf(IAstModification.InsertAfter(
decl, assign, parent as IStatementContainer
))
}
} else {
// Transform the vardecl with initvalue to a plain vardecl + assignment
// this allows for other optimizations to kick in.
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
val assign = Assignment(AssignTarget(identifier, null, null, pos), decl.value!!, AssignmentOrigin.VARINIT, pos)
decl.value = null
return listOf(IAstModification.InsertAfter(
decl, assign, parent as IStatementContainer
))
}
}
}
@ -349,8 +369,10 @@ internal class StatementReorderer(val program: Program,
val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program)!!
if(targetVar.arraysize==null)
if(targetVar.arraysize==null) {
errors.err("array has no defined size", assign.position)
return noModifications
}
if(assign.value !is IdentifierReference) {
errors.err("invalid array value to assign to other array", assign.value.position)

View File

@ -29,4 +29,28 @@ class TestVariables: FunSpec({
"""
compileText(C64Target, true, text, writeAssembly = true).assertSuccess()
}
test("array initialization with array literal") {
val text = """
main {
sub start() {
ubyte[] @shared arrayvar = [1,2,3,4]
}
}
"""
compileText(C64Target, true, text, writeAssembly = true).assertSuccess()
}
test("array initialization with array var assignment") {
val text = """
main {
sub start() {
ubyte[3] @shared arrayvar = main.values
}
ubyte[] values = [1,2,3]
}
"""
compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
}
})

View File

@ -7,9 +7,6 @@ For next release
sub sprite_y_for_row(ubyte row) -> word {
return (8-row as byte)
}
- fix crash:
word[33] sprites_x = sprites.sprites_x
word[33] sprites_y = sprites.sprites_y
- Fix: better error message for len() in:
ubyte[64] chessboard
sub init() {

View File

@ -2,13 +2,12 @@
%zeropage basicsafe
main {
; TODO why allocated not cleaned????
ubyte[255] @shared @requirezp arr1
uword[100] @shared @requirezp arr2
sub start() {
; txt.print_ub(arr1[3])
; txt.spc()
; txt.print_uw(arr2[1])
; txt.spc()
byte[3] @shared sprites_x = values
}
byte[3] values = [1,2,3]
}