From 8be0cd3c19199dcf4f62e4282aaa135ee49fc827 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 15 Mar 2026 23:55:44 +0100 Subject: [PATCH] fix certain value copying through pointer dereferences --- codeCore/src/prog8/code/core/Enumerations.kt | 2 +- .../compiler/astprocessing/AstChecker.kt | 2 +- .../compiler/astprocessing/CodeDesugarer.kt | 15 ++++++++--- .../prog8/ast/expressions/AstExpressions.kt | 26 ++++++++++++++++++- docs/source/todo.rst | 3 +-- examples/test.p8 | 26 ++++++++++++++----- 6 files changed, 59 insertions(+), 15 deletions(-) diff --git a/codeCore/src/prog8/code/core/Enumerations.kt b/codeCore/src/prog8/code/core/Enumerations.kt index 58c4608bb..9f680f467 100644 --- a/codeCore/src/prog8/code/core/Enumerations.kt +++ b/codeCore/src/prog8/code/core/Enumerations.kt @@ -212,7 +212,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType } fun dereference(): DataType { - require(isPointer || isUnsignedWord) + require(isPointer || isUnsignedWord) { "cannot dereference non-pointer type ${this}"} return when { isUnsignedWord -> forDt(BaseDataType.UBYTE) sub!=null -> forDt(sub) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 91b403fcf..9710c43aa 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -2254,7 +2254,7 @@ internal class AstChecker(private val program: Program, if(symbol==null) errors.undefined(deref.chain.take(1), position=deref.position) else - errors.err("unable to determine type of dereferenced pointer expression", deref.position) + errors.err("unable to determine type of dereferenced pointer expression, make sure it dereferences an actual pointer type", deref.position) } super.visit(deref) diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index 44f4a3ca5..734f9e236 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -974,9 +974,18 @@ _after: val sourcePtr = assignment.value as PtrDereference val targetDt = targetPtr.inferType(program) if (targetDt == sourcePtr.inferType(program)) { - val sourceAddress = IdentifierReference(sourcePtr.chain, assignment.position) - val targetAddress = IdentifierReference(targetPtr.chain, assignment.position) - + val sourceAddress: Expression + val targetAddress: Expression + if(!targetPtr.derefLast) { + val targetIdentifier = IdentifierReference(targetPtr.chain, assignment.position) + targetAddress = AddressOf(targetIdentifier, null, null, false, false, assignment.position) + } else + targetAddress = IdentifierReference(targetPtr.chain, assignment.position) + if(!sourcePtr.derefLast) { + val sourceIdentifier = IdentifierReference(sourcePtr.chain, assignment.position) + sourceAddress = AddressOf(sourceIdentifier, null, null, false, false, assignment.position) + } else + sourceAddress = IdentifierReference(sourcePtr.chain, assignment.position) val copy = when { targetDt.isLong -> FunctionCallStatement( IdentifierReference(listOf("prog8_lib_copylong"), assignment.position), diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 47e9c76aa..2c14d455b 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -553,6 +553,19 @@ data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: Array require(replacement is ArrayIndex) arrayIndex = replacement } + node===dereference -> { + if(replacement is IdentifierReference) { + identifier = replacement + dereference = null + } else if(replacement is ArrayIndex) { + arrayIndex = replacement + dereference = null + } else if(replacement is PtrDereference) { + dereference = replacement + } else { + throw FatalAstException("invalid replace $node") + } + } else -> { throw FatalAstException("invalid replace, no child node $node") } @@ -1785,7 +1798,18 @@ class PtrDereference( override fun inferType(program: Program): InferredTypes.InferredType { - fun resultType(dt: DataType?) = if(dt==null) InferredTypes.unknown() else InferredTypes.knownFor(if(derefLast) dt.dereference() else dt) + fun resultType(dt: DataType?): InferredTypes.InferredType { + return if (dt == null) InferredTypes.unknown() + else { + if(derefLast) { + if(dt.isPointer || dt.isUnsignedWord) + InferredTypes.knownFor(dt.dereference()) + else + InferredTypes.unknown() + } else + InferredTypes.knownFor(dt) + } + } val target = definingScope.lookup(chain) ?: return InferredTypes.unknown() return when (target) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index e7ac8545a..b4d53f14e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -26,12 +26,12 @@ Future Things and Ideas - structs: properly fix the symbol name prefix hack in StStruct.sameas(), see github issue 198 - struct/ptr: support const pointers (simple and struct types) (make sure to change codegen properly in all cases, change remark about this limitation in docs too) - struct/ptr: implement the remaining TODOs in PointerAssignmentsGen. -- struct/ptr: optimize the float copying in assignIndexedPointer() (also word and long?) - struct/ptr: optimize augmented assignments to indexed pointer targets like sprptr[2]^^.y++ (these are now not performend in-place but as a regular assignment) - struct/ptr: implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs') - struct/ptr: support @nosplit pointer arrays? - struct/ptr: support pointer to pointer? - struct/ptr: support for typed function pointers? (&routine could be typed by default as well then) +- struct/ptr: optimize the long pointer deref in assignPointerDerefExpression() so that it doesnt require a temp assignment all the time - struct/ptr: really 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" - struct/ptr: (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) - make memory mapped variables support more constant expressions such as: &uword MyHigh = &mylong1+2 (see github issue #192) @@ -50,7 +50,6 @@ Future Things and Ideas - Kotlin: can we use inline value classes in certain spots? (domain types instead of primitives) - Kotlin: can private setters / backing fields be used? (internal mutableList, external List) - add float support to the configurable compiler targets. Restrictions: just have "cbm-style floats" as an option (to that it can slot into the current float codegen), where all you have to specify is the addresses of AYINT and GIVAYF and FADDT and all their friends. -- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions - Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first), maybe only when qualified symbol starts with '.' such as: .local.value = 33 - something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using '? - Improve register load order in subroutine call args assignments: diff --git a/examples/test.p8 b/examples/test.p8 index dc2e0ba11..e3f69ef3b 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,18 +1,30 @@ %import textio %zeropage basicsafe +%option no_sysinit main { - sub start() { - long[] larray = [111111, 222222, 333333, 444444] - test(larray) + struct Node { + bool x + word y + long lvar } - sub test(^^long lv) { - txt.print_l(lv^^) + sub start() { + ^^Node nodeptr1 = ^^Node : [false, 1111, 1111111] + ^^Node nodeptr2 = ^^Node : [false, 2222, 2222222] + long @shared lvar = 333333 + + txt.print_l(nodeptr1^^.lvar) txt.spc() - lv++ - txt.print_l(lv^^) + txt.print_l(nodeptr2^^.lvar) + txt.nl() + + nodeptr1^^.lvar = nodeptr2^^.lvar + + txt.print_l(nodeptr1^^.lvar) + txt.spc() + txt.print_l(nodeptr2^^.lvar) txt.nl() } }