From 2bbbacc46ea280b106fa2d1b0fb1345d642eb603 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 24 Aug 2026 19:21:30 +0200 Subject: [PATCH] normal error message instead of crash when typed pointer arrays are used as struct field (currently not supported by the compiler) --- codeCore/src/prog8/code/core/DataTypes.kt | 5 +++++ .../compiler/astprocessing/AstChecker.kt | 21 ++++++++++++------- docs/source/structpointers.rst | 8 +++++-- docs/source/todo.rst | 2 ++ 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/codeCore/src/prog8/code/core/DataTypes.kt b/codeCore/src/prog8/code/core/DataTypes.kt index 67c1abcde..0a76a10ca 100644 --- a/codeCore/src/prog8/code/core/DataTypes.kt +++ b/codeCore/src/prog8/code/core/DataTypes.kt @@ -219,6 +219,11 @@ class DataType private constructor( fun elementToArray(splitwords: Boolean = true): DataType { return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords) + else if(base.isPointer) { + if(subType!=null) arrayOfPointersTo(subType) + else if(subTypeFromAntlr!=null) arrayOfPointersFromAntlrTo(sub, subTypeFromAntlr) + else arrayOfPointersTo(sub!!) + } else arrayFor(base, false) } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 1791a3c14..59dfcb8a5 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -2331,14 +2331,21 @@ internal class AstChecker(private val program: Program, if(uniqueFields.isEmpty()) errors.err("struct must contain at least one field", struct.position) + fun validFieldType(field: StructField): Boolean { + val dt=field.type + if(dt.isArray && dt.subType!=null) { + errors.err("arrays of typed pointers are currently not supported, use untyped pointer for now at field '${field.name}'", struct.position) + return false + } + return dt.isBasic + || dt.base.isNumericOrBool + || dt.isPointer + || dt.isArray + } + struct.fields.forEach { field -> - val dt = field.type - val allowed = dt.isBasic - || dt.base.isNumericOrBool - || dt.isPointer - || (dt.isArray && dt.sub!!.isNumericOrBool) - if(!allowed) - errors.err("only booleans, numeric and pointer fields allowed in a struct: '${field.name}'", struct.position) + if(!validFieldType(field)) + errors.err("invalid struct field type at '${field.name}'", struct.position) } } diff --git a/docs/source/structpointers.rst b/docs/source/structpointers.rst index fe18430ec..3ba2ddaf9 100644 --- a/docs/source/structpointers.rst +++ b/docs/source/structpointers.rst @@ -140,8 +140,12 @@ dealing with all of them separately. You first define the struct type like so:: bool elite } -You can use boolean fields, numeric fields (byte, word, long, float), and pointer fields (including str, which is translated into ^^ubyte). -You cannot nest struct types, but inline arrays are supported as fields. 2D arrays are not allowed as struct fields. +Allowed field datatypes: all numeric types (``byte``, ``ubyte``, ``word``, ``uword``, ``long``, ``float``), +``bool``, ``str``, typed pointers to those or to other structs, and inline 1D arrays of these element types. +Not allowed: struct instances themselves (structs cannot be nested, use a pointer to the struct instead), +arrays of typed pointers such as ``^^Enemy[4]`` (use untyped pointers instead), and 2D arrays. +The typed pointer array and 2D array restrictions are limitations of the current compiler; they may or may not be lifted in a future version. + Fields in a struct are 'packed' (meaning the values are placed back-to-back in memory), and placed in memory in order of declaration. This guarantees exact size and place of the fields. ``sizeof()`` knows how to calculate the combined size of a struct, and ``offsetof()`` can be used to get the byte offset of a given field in the struct. The size of a struct cannot exceed 1 memory page (256 bytes). diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 29c38b8df..298c40f4b 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -9,6 +9,8 @@ Future Things and Ideas - symboldump: some sort of javadocs generated from the p8 source files (instead of just the function signatures). Use markdown for formatting, not html. - when implementing unsigned longs: remove the (multiple?) "TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast - struct/ptr: implement the remaining TODOs in PointerAssignmentsGen. +- struct/ptr: support arrays of typed pointers as struct fields (e.g. ``^^Enemy[4] foos``); currently rejected by AstChecker with "arrays of typed pointers are currently not supported", untyped pointers must be used instead. +- struct/ptr: support 2D arrays as struct fields; currently a parser error ("2D arrays are not allowed as struct fields"), even though 2D arrays are supported for regular variables (see variables.rst). - struct/ptr: typed function pointers (simplified): ``&&subroutine`` returns opaque typed pointer (no ``funcptr`` keyword), assignment allowed where target type is inferred, calling via ``ptr(args)``. No explicit signature syntax. Useful mostly for 68000 targets. - struct/ptr: really fixing the pointer dereferencing issues (cursed hybrid between 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 as assignment target (LHS). RHS value case ``a = func().field`` / ``a = func().next.field`` now works for both VM and 6502 via generic ``peek`` desugar (``AstExpressions.kt:280``, ``CodeDesugarer.kt:654``); still fails for ``func().field = a`` / ``func().next.field = a`` which requires ``Prog8ANTLR.g4:239`` ``assign_target`` grammar change to allow ``functioncall '.' field`` chain (see ``compiler/test/TestPointers.kt`` xtest).