diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 3c610585d..32ade98d3 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1970,15 +1970,8 @@ internal class AstChecker(private val program: Program, } override fun visit(memread: DirectMemoryRead) { - if(!(memread.addressExpression.inferType(program) issimpletype BaseDataType.UWORD)) { - errors.err("address for memory access isn't uword", memread.position) - } - val tc = memread.addressExpression as? TypecastExpression - if(tc!=null && tc.implicit) { - if(!(tc.expression.inferType(program) issimpletype BaseDataType.UWORD)) { - errors.err("address for memory access isn't uword", memread.position) - } - } + if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program)) + errors.err("invalid address type for memory access", memread.position) val pointervar = memread.addressExpression as? IdentifierReference if(pointervar!=null) @@ -1990,16 +1983,22 @@ internal class AstChecker(private val program: Program, super.visit(memread) } - override fun visit(memwrite: DirectMemoryWrite) { - if(!(memwrite.addressExpression.inferType(program) issimpletype BaseDataType.UWORD)) { - errors.err("address for memory access isn't uword", memwrite.position) - } - val tc = memwrite.addressExpression as? TypecastExpression + private fun allowedMemoryAccessAddressExpression(addressExpression: Expression, program: Program): Boolean { + val dt = addressExpression.inferType(program) + if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub==BaseDataType.UBYTE)) + return true + val tc = addressExpression as? TypecastExpression if(tc!=null && tc.implicit) { - if(!(tc.expression.inferType(program) issimpletype BaseDataType.UWORD)) { - errors.err("address for memory access isn't uword", memwrite.position) - } + val dt = tc.expression.inferType(program) + if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub==BaseDataType.UBYTE)) + return true } + return false + } + + override fun visit(memwrite: DirectMemoryWrite) { + if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program)) + errors.err("invalid address type for memory access", memwrite.position) val pointervar = memwrite.addressExpression as? IdentifierReference if(pointervar!=null) @@ -2355,8 +2354,10 @@ internal class AstChecker(private val program: Program, if(sourceDatatype.isPointer) { if(!(sourceDatatype isAssignableTo targetDatatype)) errors.err("cannot assign different pointer type", position) + } else if(sourceDatatype.isString && targetDatatype.sub?.isByte==true) { + // assigning a string to a byte pointer is allowed. } else if(!sourceDatatype.isUnsignedWord && !sourceDatatype.isStructInstance) - errors.err("can only assign uword or correct pointer type to a pointer", position) + errors.err("incompatible value type, can only assign uword or correct pointer type", position) } else if(targetDatatype.isString && sourceDatatype.isUnsignedWord) errors.err("can't assign uword to str. If the source is a string pointer and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position) diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index 778ad02aa..b6c720894 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -26,6 +26,7 @@ internal class CodeDesugarer(val program: Program, private val target: ICompilat // - remove alias nodes // - convert on..goto/call to jumpaddr array and separate goto/call // - replace implicit pointer dereference chains (a.b.c.d) with explicit ones (a^^.b^^.c^^.d) + // - replace ptr^^ by @(ptr) if ptr is just an uword. override fun after(alias: Alias, parent: Node): Iterable { return listOf(IAstModification.Remove(alias, parent as IStatementContainer)) @@ -451,4 +452,17 @@ _after: , ongoto.position) return listOf(IAstModification.ReplaceNode(ongoto, replacementScope, parent)) } + + override fun after(deref: PtrDereference, parent: Node): Iterable { + // TODO what about DirectMemoryWrite ?? (LHS of assignment?) + if(deref.field==null && deref.chain.isEmpty()) { + val varDt = deref.identifier.targetVarDecl()?.datatype + if(varDt?.isUnsignedWord==true || (varDt?.isPointer==true && varDt.sub==BaseDataType.UBYTE)) { + // replace ptr^^ by @(ptr) when ptr is uword or ^^byte + val memread = DirectMemoryRead(deref.identifier, deref.position) + return listOf(IAstModification.ReplaceNode(deref, memread, parent)) + } + } + return noModifications + } } diff --git a/compiler/test/TestPointers.kt b/compiler/test/TestPointers.kt index 7974f4e8c..a640c44da 100644 --- a/compiler/test/TestPointers.kt +++ b/compiler/test/TestPointers.kt @@ -243,7 +243,7 @@ main { compileText(VMTarget(), false, src, outputDir, errors=errors) val err = errors.errors err.size shouldBe 1 - err[0] shouldContain("15:16: can only assign uword or correct pointer type to a pointer") + err[0] shouldContain("15:16: incompatible value type, can only assign uword or correct pointer") } }) \ No newline at end of file diff --git a/compiler/test/codegeneration/TestVariousCodeGen.kt b/compiler/test/codegeneration/TestVariousCodeGen.kt index 030bd80d9..23d18f1b5 100644 --- a/compiler/test/codegeneration/TestVariousCodeGen.kt +++ b/compiler/test/codegeneration/TestVariousCodeGen.kt @@ -131,7 +131,7 @@ main { val errors = ErrorReporterForTests() compileText(C64Target(), false, text, outputDir, writeAssembly = true, errors = errors) errors.errors.size shouldBe 2 - errors.errors[0] shouldContain "isn't uword" + errors.errors[0] shouldContain "invalid address type" errors.errors[1] shouldContain "undefined symbol: doesnotexist" } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index abff3b82d..f59289675 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -44,8 +44,10 @@ STRUCTS and TYPED POINTERS - DONE: existing ARRAY type remains unchanged (it doesn't become a typed pointer) so we can keep doing register-indexed LDA array,Y addressing directly on them. - DONE: passing STR to a subroutine: parameter type becomes ^^UBYTE (rather than UWORD) (we still lose the bounds check) - DONE: passing ARRAY to a subroutine: parameter type becomes ^^ElementDt (rather than UWORD) (we still lose the bounds check) +- DONE: @(ptr) complains that ptr is not uword when ptr is ^^ubyte (should be allowed) - fix actual _msb/_lsb storage of the split-words pointer-arrays - STR should be asssignment compatible with UBYTE^^ but local scoped STR should still be accessed directly using LDA str,Y instead of through the pointer, like arrays. +- what about uword^^ -> @(uword) when it's assignment target (LHS)? - make typeForAddressOf() be even more specific about the typed pointers it returns for the address-of operator. + unit test. Needs fixes in 6502 codegen too though... (also recheck passing STR and ARRAY types to subroutines) - fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator" - (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field) diff --git a/examples/test.p8 b/examples/test.p8 index 9036b2233..7cad3f304 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -10,9 +10,25 @@ main { word[] @nosplit values = [111,222,-999,-888] - stringinfo("hello") + ^^byte @shared bptr + ^^ubyte @shared ubptr + + str name = "irmen" + stringinfo1("hello") + stringinfo2(name) + stringinfo3("apple") arrayinfo(values) arrayinfo(&values[2]) + + + bptr = name + ubptr = name + txt.print_uw(&name) + txt.spc() + txt.print_uw(bptr) + txt.spc() + txt.print_uw(ubptr) + txt.nl() } sub info(^^thing.Node node) { @@ -32,7 +48,8 @@ main { txt.nl() } - sub stringinfo(^^ubyte message) { + sub stringinfo1(^^ubyte message) { + txt.print("string1: ") txt.print_uw(message) txt.spc() txt.print(message) @@ -44,6 +61,32 @@ main { txt.nl() } + sub stringinfo2(str message) { + txt.print("string2: ") + txt.print_uw(message) + txt.spc() + txt.print(message) + txt.spc() + do { + txt.chrout(message^^) + message++ + } until message^^==0 + txt.nl() + } + + sub stringinfo3(uword message) { + txt.print("string3: ") + txt.print_uw(message) + txt.spc() + txt.print(message) + txt.spc() + do { + txt.chrout(message^^) ; equivalent to @(message) in this case + message++ + } until message^^==0 ; equivalent to @(message) in this case + txt.nl() + } + sub arrayinfo(^^word valueptr) { txt.print_uw(valueptr) txt.spc()