mirror of
https://github.com/irmen/prog8.git
synced 2025-10-25 05:18:38 +00:00
fix type checks for wrong pointer types in pointer array initalizer and assignment
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
247
examples/pointers/hashtable.p8
Normal file
247
examples/pointers/hashtable.p8
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user