fix type checks for wrong pointer types in pointer array initalizer and assignment

This commit is contained in:
Irmen de Jong
2025-09-16 21:18:29 +02:00
parent 633d6c34e2
commit ddf6e84a1a
6 changed files with 278 additions and 16 deletions

View File

@@ -1269,7 +1269,7 @@ internal class AstChecker(private val program: Program,
val arrayspec = ArrayIndex.forArray(array)
checkValueTypeAndRangeArray(array.type.getOrUndef(), arrayspec, array)
} else {
errors.err("undefined array type (multiple element types?)", array.position)
errors.err("undefined array type (multiple or incompatible element types?)", array.position)
}
if(array.parent is VarDecl) {
@@ -1277,6 +1277,17 @@ internal class AstChecker(private val program: Program,
errors.err("initialization value contains non-constant elements", array.value[0].position)
}
val elementDt = (array.parent as VarDecl).datatype.elementType()
if(elementDt.isPointer) {
// all elements in the initializer array should be of the same element type
array.value.forEach {
val valueDt = it.inferType(program).getOrUndef()
if(!valueDt.isUnsignedWord && valueDt != elementDt) {
errors.err("struct initializer element has invalid type, expected $elementDt or uword but got $valueDt", it.position)
}
}
}
} else if(array.parent is ForLoop) {
if (!array.value.all { it.constValue(program) != null })
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
@@ -2449,7 +2460,7 @@ internal class AstChecker(private val program: Program,
else if (targetDatatype.isPointer) {
if(sourceDatatype.isPointer) {
if(!(sourceDatatype isAssignableTo targetDatatype))
errors.err("cannot assign different pointer type, expected $targetDatatype got $sourceDatatype", position)
errors.err("cannot assign different pointer type, expected $targetDatatype or uword but got $sourceDatatype", position)
} else if(sourceDatatype.isString && targetDatatype.sub?.isByte==true) {
// assigning a string to a byte pointer is allowed.
} else if(!sourceDatatype.isUnsignedWord && !sourceDatatype.isStructInstance)

View File

@@ -1845,10 +1845,13 @@ main {
}"""
val errors=ErrorReporterForTests()
compileText(C64Target(), false, src, outputDir, errors=errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "wrong pointer type in array"
errors.errors[1] shouldContain "wrong pointer type in array"
// TODO check that they belong to onlynodes and node
errors.errors.size shouldBe 6
errors.errors[0] shouldContain "11:30: initialization value for pointer array"
errors.errors[1] shouldContain "11:30: undefined array type" // a bit redundant but can't be helped
errors.errors[2] shouldContain "13:13: struct initializer element has invalid type"
errors.errors[3] shouldContain "16:28: invalid assignment value"
errors.errors[4] shouldContain "16:28: undefined array type"
errors.errors[5] shouldContain "21:23: cannot assign different pointer type"
}
test("local and global struct pointer qualified name lookups") {
@@ -2075,8 +2078,8 @@ main {
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "7:24: cannot assign different pointer type, expected ^^uword got ^^ubyte"
errors.errors[1] shouldContain "10:24: cannot assign different pointer type, expected ^^ubyte got ^^uword"
errors.errors[0] shouldContain "7:24: cannot assign different pointer type, expected ^^uword or uword but got ^^ubyte"
errors.errors[1] shouldContain "10:24: cannot assign different pointer type, expected ^^ubyte or uword but got ^^uword"
}
test("passing nosplit array of structpointers to a subroutine in various forms should be param type ptr to struct") {

View File

@@ -1841,7 +1841,13 @@ class StaticStructInitializer(var structname: IdentifierReference,
override fun referencesIdentifier(nameInSource: List<String>): Boolean = structname.referencesIdentifier(nameInSource) || args.any{it.referencesIdentifier(nameInSource)}
override fun inferType(program: Program) = InferredTypes.knownFor(BaseDataType.UWORD)
override fun inferType(program: Program): InferredTypes.InferredType {
val struct = structname.targetStructDecl()
return if(struct==null)
InferredTypes.unknown()
else
InferredTypes.knownFor(DataType.pointer(struct))
}
}

View File

@@ -58,7 +58,6 @@ and for example the below code omits line 5::
STRUCTS and TYPED POINTERS
--------------------------
- fix type checks for wrong pointer types in pointer array initalizer and assignment
- fix the pointers/hashtable.p8 example
- fix code size regressions (if any left)
- optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
@@ -73,6 +72,7 @@ STRUCTS and TYPED POINTERS
- support for typed function pointers? (&routine could be typed by default as well then)
- support @nosplit pointer arrays?
- support pointer to pointer?
- the type of struct initializer arrays should not be uword[] but ^^struct[]
- 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"
- (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)

View File

@@ -0,0 +1,247 @@
; Doubly Linked List with Hash Table Cache
; Demonstrates advanced pointer usage with a combination of doubly linked lists and hash tables
; for efficient data storage and retrieval
; TODO fix this AI generated code
%import math
%import strings
%import textio
%zeropage basicsafe
main {
sub start() {
txt.print("Doubly Linked List with Hash Table Cache Demo\n")
txt.print("============================================\n\n")
; Initialize the data structure
cache.init()
; Add some sample data
txt.print("Adding sample data...\n")
cache.add("Alice", "Engineer", 30)
cache.add("Bob", "Designer", 25)
cache.add("Charlie", "Manager", 35)
cache.add("Diana", "Developer", 28)
cache.add("Eve", "Analyst", 32)
txt.print("\nForward traversal:\n")
cache.print_forward()
txt.print("\nBackward traversal:\n")
cache.print_backward()
txt.print("\nSearching for specific entries:\n")
txt.print("Looking for 'Charlie': ")
^^cache.Entry entry = cache.find("Charlie")
if entry!=0 {
txt.print(entry.name)
txt.print(" - ")
txt.print(entry.job)
txt.print(" (")
txt.print_ub(entry.age)
txt.print(" years old)\n")
} else {
txt.print("Not found\n")
}
txt.print("Looking for 'Frank': ")
entry = cache.find("Frank")
if entry!=0 {
txt.print("Found\n")
} else {
txt.print("Not found\n")
}
txt.print("\nRemoving 'Bob'...\n")
void cache.remove("Bob")
txt.print("Forward traversal after removal:\n")
cache.print_forward()
txt.print("\nDemonstrating hash collision handling...\n")
; Add entries that will likely collide in the hash table
cache.add("A", "First", 20)
cache.add("B", "Second", 21)
cache.add("C", "Third", 22)
txt.print("Added entries with potential hash collisions.\n")
txt.print("Forward traversal:\n")
cache.print_forward()
}
}
cache {
struct Entry {
^^Entry next ; Next entry in the list
^^Entry prev ; Previous entry in the list
^^Entry hash_next ; Next entry in the hash bucket
str name ; Name (key)
str job ; Job description
ubyte age ; Age
}
const ubyte HASH_TABLE_SIZE = 16
^^Entry[HASH_TABLE_SIZE] hash_table ; Hash table for fast lookups
^^Entry head = 0 ; Head of the doubly linked list
^^Entry tail = 0 ; Tail of the doubly linked list
uword count = 0 ; Number of entries
sub init() {
; Initialize hash table buckets to null
ubyte i
for i in 0 to HASH_TABLE_SIZE-1
hash_table[i] = 0
}
sub hash(str key) -> ubyte {
; Simple hash function
ubyte hash_value = 0
ubyte i = 0
while key[i] != 0 {
hash_value = hash_value * 31 + key[i]
i++
}
return hash_value % HASH_TABLE_SIZE
}
sub add(str name, str job, ubyte age) {
; Create new entry
^^Entry new_entry = arena.alloc(sizeof(Entry))
ubyte name_len = strings.length(name)
ubyte job_len = strings.length(job)
^^ubyte name_copy = arena.alloc(name_len + 1)
^^ubyte job_copy = arena.alloc(job_len + 1)
void strings.copy(name, name_copy)
void strings.copy(job, job_copy)
new_entry.name = name_copy
new_entry.job = job_copy
new_entry.age = age
new_entry.next = 0
new_entry.prev = 0
new_entry.hash_next = 0
; Add to the end of the doubly linked list
if head == 0 {
; First entry
head = new_entry
tail = new_entry
} else {
; Add to the end
tail.next = new_entry
new_entry.prev = tail
tail = new_entry
}
; Add to hash table
ubyte bucket = hash(name)
new_entry.hash_next = hash_table[bucket]
hash_table[bucket] = new_entry
count++
txt.print("Added: ")
txt.print(name)
txt.print("\n")
}
sub find(str name) -> ^^Entry {
; Find entry using hash table for O(1) average case
ubyte bucket = hash(name)
^^Entry current = hash_table[bucket]
while current != 0 {
if strings.compare(current.name, name) == 0
return current
current = current.hash_next
}
return 0 ; Not found
}
sub remove(str name) -> bool {
; Find the entry
^^Entry to_remove = find(name)
if to_remove == 0
return false ; Not found
; Remove from doubly linked list
if to_remove.prev != 0
to_remove.prev.next = to_remove.next
else
head = to_remove.next ; Was the head
if to_remove.next != 0
to_remove.next.prev = to_remove.prev
else
tail = to_remove.prev ; Was the tail
; Remove from hash table
ubyte bucket = hash(name)
if hash_table[bucket] == to_remove {
hash_table[bucket] = to_remove.hash_next
} else {
^^Entry current = hash_table[bucket]
while current.hash_next != 0 {
if current.hash_next == to_remove {
current.hash_next = to_remove.hash_next
break
}
current = current.hash_next
}
}
count--
txt.print("Removed: ")
txt.print(name)
txt.print("\n")
return true
}
sub print_forward() {
^^Entry current = head
while current != 0 {
txt.print("- ")
txt.print(current.name)
txt.print(" (")
txt.print(current.job)
txt.print(", ")
txt.print_ub(current.age)
txt.print(")\n")
current = current.next
}
txt.print("Total entries: ")
txt.print_uw(count)
txt.print("\n")
}
sub print_backward() {
^^Entry current = tail
while current != 0 {
txt.print("- ")
txt.print(current.name)
txt.print(" (")
txt.print(current.job)
txt.print(", ")
txt.print_ub(current.age)
txt.print(")\n")
current = current.prev
}
txt.print("Total entries: ")
txt.print_uw(count)
txt.print("\n")
}
}
arena {
; Simple arena allocator
uword buffer = memory("arena", 8000, 0)
uword next = buffer
sub alloc(ubyte size) -> uword {
defer next += size
return next
}
}

View File

@@ -16,14 +16,9 @@ main {
^^Node[] @shared nodeswithtype = [
^^Node: [1,"one", 1000, true, 1.111],
^^Node: [],
^^Foobar: [] ; TODO fix so that type error
]
^^Node derp = ^^Foobar: [] ; TODO fix so that type error
^^Node derp2 = ^^Node: []
^^ubyte bptr = derp2 as ^^ubyte
bptr = derp as ^^ubyte
^^Node derp2 = ^^Foobar: []
^^Node[] @shared nodeswithout = [
[2,"two", 2000, false, 2.222],