mirror of
https://github.com/irmen/prog8.git
synced 2025-08-09 03:25:24 +00:00
no longer take AddressOf a str-variable that is a subroutine's parameter with str type (it's just an address/uword already)
This commit is contained in:
@@ -5,11 +5,13 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.internedStringsModuleName
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.compiler.astprocessing.isInRegularRAMof
|
||||
import prog8.compiler.astprocessing.isSubroutineParameter
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
|
||||
|
||||
@@ -31,6 +33,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@@ -77,7 +80,6 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
|
||||
if(!subroutine.isAsmSubroutine) {
|
||||
// change 'str' parameters into 'uword' (just treat it as an address)
|
||||
// TODO fix [TypecastsAdder] to treat str param vars as uword instead of adding casts/addressof (which is wrong for str *parameters*)
|
||||
val stringParams = subroutine.parameters.filter { it.type==DataType.STR }
|
||||
val parameterChanges = stringParams.map {
|
||||
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
|
||||
@@ -167,7 +169,8 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
// see if we can remove superfluous typecasts (outside of expressions)
|
||||
// such as casting byte<->ubyte, word<->uword
|
||||
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of.
|
||||
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of,
|
||||
// UNLESS it's a str parameter in the containing subroutine - then we remove the typecast altogether
|
||||
val sourceDt = typecast.expression.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
||||
|| typecast.type in WordDatatypes && sourceDt in WordDatatypes) {
|
||||
@@ -176,22 +179,23 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Note: for various reasons (most importantly, code simplicity), the code generator assumes/requires
|
||||
// that the types of assignment values and their target are the same,
|
||||
// and that the types of both operands of a binaryexpression node are the same.
|
||||
// So, it is not easily possible to remove the typecasts that are there to make these conditions true.
|
||||
// The only place for now where we can do this is for:
|
||||
// asmsub register pair parameter.
|
||||
|
||||
if(sourceDt in PassByReferenceDatatypes) {
|
||||
if(typecast.type==DataType.UWORD) {
|
||||
if(typecast.expression is IdentifierReference) {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
val identifier = typecast.expression as? IdentifierReference
|
||||
if(identifier!=null) {
|
||||
return if(identifier.isSubroutineParameter(program)) {
|
||||
listOf(IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
AddressOf(typecast.expression as IdentifierReference, typecast.position),
|
||||
typecast.expression,
|
||||
parent
|
||||
))
|
||||
))
|
||||
} else {
|
||||
listOf(IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
AddressOf(identifier, typecast.position),
|
||||
parent
|
||||
))
|
||||
}
|
||||
} else if(typecast.expression is IFunctionCall) {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
|
@@ -105,7 +105,8 @@ fun compileProgram(filepath: Path,
|
||||
)
|
||||
postprocessAst(programAst, errors, compilationOptions)
|
||||
|
||||
// printAst(programAst)
|
||||
// println("*********** AST BEFORE ASSEMBLYGEN *************")
|
||||
// printAst(programAst)
|
||||
|
||||
if (writeAssembly) {
|
||||
val result = writeAssembly(programAst, errors, outputDir, compilationOptions)
|
||||
@@ -346,7 +347,7 @@ private fun writeAssembly(programAst: Program,
|
||||
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
|
||||
printAst(programAst) // TODO
|
||||
// printAst(programAst)
|
||||
|
||||
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
|
||||
val assembly = asmGeneratorFor(compilerOptions.compTarget,
|
||||
|
@@ -209,3 +209,12 @@ internal fun AssignTarget.isInRegularRAMof(machine: IMachineDefinition): Boolean
|
||||
else -> return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolean {
|
||||
val vardecl = this.targetVarDecl(program)
|
||||
if(vardecl!=null && vardecl.autogeneratedDontRemove) {
|
||||
return vardecl.definingSubroutine?.parameters?.any { it.name==vardecl.name } == true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@@ -135,11 +135,13 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
||||
TypecastExpression(pair.second, requiredType, true, pair.second.position),
|
||||
call as Node)
|
||||
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
||||
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
||||
if(pair.second is IdentifierReference) {
|
||||
// We allow STR/ARRAY values in place of UWORD parameters.
|
||||
// Take their address instead, UNLESS it's a str parameter in the containing subroutine
|
||||
val identifier = pair.second as? IdentifierReference
|
||||
if(identifier?.isSubroutineParameter(program)==false) {
|
||||
modifications += IAstModification.ReplaceNode(
|
||||
call.args[index],
|
||||
AddressOf(pair.second as IdentifierReference, pair.second.position),
|
||||
AddressOf(identifier, pair.second.position),
|
||||
call as Node)
|
||||
}
|
||||
} else if(pair.second is NumericLiteralValue) {
|
||||
|
@@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
@@ -17,29 +18,6 @@ import kotlin.test.*
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class TestSubroutines {
|
||||
|
||||
@Test
|
||||
fun arrayParameterNotYetAllowed_ButShouldPerhapsBe() {
|
||||
// note: the *parser* accepts this as it is valid *syntax*,
|
||||
// however, it's not (yet) valid for the compiler
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
}
|
||||
|
||||
asmsub asmfunc(ubyte[] thing @AY) {
|
||||
}
|
||||
|
||||
sub func(ubyte[22] thing) {
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, errors, false).assertFailure("currently array dt in signature is invalid") // TODO should not be invalid?
|
||||
assertEquals(0, errors.warnings.size)
|
||||
assertContains(errors.errors.single(), ".p8:9:16: Non-string pass-by-reference types cannot occur as a parameter type directly")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stringParameter() {
|
||||
val text = """
|
||||
@@ -80,11 +58,11 @@ class TestSubroutines {
|
||||
assertEquals(DataType.STR, paramvar.datatype)
|
||||
val t2var = func.statements[1] as VarDecl
|
||||
assertEquals("t2", t2var.name)
|
||||
assertTrue(t2var.value is IdentifierReference, "str param in function body should be treated as plain uword")
|
||||
assertEquals("thing", (t2var.value as IdentifierReference).nameInSource.single())
|
||||
assertTrue(t2var.value is TypecastExpression, "str param in function body should not be transformed by normal compiler steps")
|
||||
assertEquals(DataType.UWORD, (t2var.value as TypecastExpression).type)
|
||||
val call = func.statements[2] as FunctionCallStatement
|
||||
assertEquals("asmfunc", call.target.nameInSource.single())
|
||||
assertTrue(call.args.single() is IdentifierReference, "str param in function body should be treated as plain uword")
|
||||
assertTrue(call.args.single() is IdentifierReference, "str param in function body should not be transformed by normal compiler steps")
|
||||
assertEquals("thing", (call.args.single() as IdentifierReference).nameInSource.single())
|
||||
}
|
||||
|
||||
@@ -131,14 +109,37 @@ class TestSubroutines {
|
||||
assertEquals(DataType.UWORD, paramvar.datatype, "pre-asmgen should have changed str to uword type")
|
||||
val t2var = func.statements[1] as VarDecl
|
||||
assertEquals("t2", t2var.name)
|
||||
assertTrue(t2var.value is IdentifierReference, "str param in function body should be treated as plain uword")
|
||||
assertTrue(t2var.value is IdentifierReference, "str param in function body should be treated as plain uword before asmgen")
|
||||
assertEquals("thing", (t2var.value as IdentifierReference).nameInSource.single())
|
||||
val call = func.statements[2] as FunctionCallStatement
|
||||
assertEquals("asmfunc", call.target.nameInSource.single())
|
||||
assertTrue(call.args.single() is IdentifierReference, "str param in function body should be treated as plain uword")
|
||||
assertTrue(call.args.single() is IdentifierReference, "str param in function body should be treated as plain uword and not been transformed")
|
||||
assertEquals("thing", (call.args.single() as IdentifierReference).nameInSource.single())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun arrayParameterNotYetAllowed_ButShouldPerhapsBe() {
|
||||
// note: the *parser* accepts this as it is valid *syntax*,
|
||||
// however, it's not (yet) valid for the compiler
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
}
|
||||
|
||||
asmsub asmfunc(ubyte[] thing @AY) {
|
||||
}
|
||||
|
||||
sub func(ubyte[22] thing) {
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, errors, false).assertFailure("currently array dt in signature is invalid") // TODO should not be invalid?
|
||||
assertEquals(0, errors.warnings.size)
|
||||
assertContains(errors.errors.single(), ".p8:9:16: Non-string pass-by-reference types cannot occur as a parameter type directly")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("TODO: allow array parameter in signature") // TODO allow this?
|
||||
fun arrayParameter() {
|
||||
|
@@ -11,10 +11,11 @@ main {
|
||||
@($2002) = 'c'
|
||||
@($2003) = 0
|
||||
|
||||
asmfunc("text")
|
||||
asmfunc("text12345")
|
||||
asmfunc(text)
|
||||
asmfunc($2000)
|
||||
func("text")
|
||||
txt.nl()
|
||||
func("text12345")
|
||||
func(text)
|
||||
func($2000)
|
||||
}
|
||||
@@ -27,10 +28,11 @@ main {
|
||||
}}
|
||||
}
|
||||
|
||||
; TODO fix asmgen when using 'str' type
|
||||
sub func(uword thing) {
|
||||
sub func(str thing) {
|
||||
uword t2 = thing as uword
|
||||
ubyte length = string.length(thing)
|
||||
txt.print_uwhex(thing, true)
|
||||
txt.nl()
|
||||
txt.print_ub(length)
|
||||
txt.nl()
|
||||
txt.print(thing)
|
||||
|
Reference in New Issue
Block a user