From b1e07f3fdb2baef0b0e45da48d3973abaacc3a8f Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 30 Oct 2025 20:58:21 +0100 Subject: [PATCH] better error when trying to use a const pointer (which is not supported yet) --- .../codegen/cpu6502/ProgramAndVarsGen.kt | 2 +- .../prog8/codegen/intermediate/StConvert.kt | 3 +- .../compiler/astprocessing/AstChecker.kt | 4 +- .../compiler/astprocessing/VariousCleanups.kt | 61 +++---- compiler/test/TestPointers.kt | 20 ++- compiler/test/TestSymbolTable.kt | 4 +- docs/source/todo.rst | 1 + examples/test.p8 | 149 +++--------------- .../src/prog8/intermediate/IRFileWriter.kt | 1 + simpleAst/src/prog8/code/SymbolTable.kt | 2 +- simpleAst/src/prog8/code/SymbolTableMaker.kt | 2 +- 11 files changed, 75 insertions(+), 174 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 22a485bba..0f863f1e6 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -938,7 +938,7 @@ internal class ProgramAndVarsGen( asmgen.out(" ${it.name} = ${it.address.toHex()}") } consts.sortedBy { it.name }.forEach { - if(it.dt==BaseDataType.FLOAT) + if(it.dt.isFloat) asmgen.out(" ${it.name} = ${it.value}") else asmgen.out(" ${it.name} = ${it.value.toHex()}") diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt index fc7d1bf14..69fb2ede6 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/StConvert.kt @@ -122,7 +122,6 @@ private fun convert(variable: StMemVar): IRStMemVar { private fun convert(constant: StConstant): IRStConstant { - val dt = DataType.forDt(constant.dt) val scopedName = if('.' in constant.name) { constant.name } else { @@ -132,7 +131,7 @@ private fun convert(constant: StConstant): IRStConstant { constant.name } } - return IRStConstant(scopedName, dt, constant.value) + return IRStConstant(scopedName, constant.dt, constant.value) } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 87f24e4eb..35d0fb15e 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -874,9 +874,9 @@ internal class AstChecker(private val program: Program, err("recursive var declaration") // CONST can only occur on simple types (byte, word, float) - if(decl.type== VarDeclType.CONST) { + if(decl.type==VarDeclType.CONST) { if (!decl.datatype.isNumericOrBool) - err("const can only be used on numeric types or booleans") + err("const can only be used on numbers and booleans") } // FLOATS enabled? diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 17ae09bd8..471f94ba6 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -27,45 +27,46 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, if(valueType.isUnknown) return noModifications val valueDt = valueType.getOrUndef() - when(decl.type) { - VarDeclType.VAR -> { - if(decl.isArray) { - // using a array of words as initializer to a pointer array is fine - if (!valueDt.isSplitWordArray || !decl.datatype.isPointerArray) - errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position) - } else if(!decl.datatype.isString) { - if (valueDt.largerSizeThan(decl.datatype)) { - val constValue = decl.value?.constValue(program) - if (constValue != null) - errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position) - else - errors.err("value out of range for ${decl.datatype}", decl.value!!.position) + if(!(valueDt.isWord && decl.datatype.isPointer)) { + when(decl.type) { + VarDeclType.VAR -> { + if(decl.isArray) { + // using a array of words as initializer to a pointer array is fine + if (!valueDt.isSplitWordArray || !decl.datatype.isPointerArray) + errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position) + } else if(!decl.datatype.isString) { + if (valueDt.largerSizeThan(decl.datatype)) { + val constValue = decl.value?.constValue(program) + if (constValue != null) + errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position) + else + errors.err("value out of range for ${decl.datatype}", decl.value!!.position) + } } } - } - VarDeclType.CONST -> { - // change the vardecl type itself as well, but only if new type is smaller - if(valueDt.largerSizeThan(decl.datatype)) { - val constValue = decl.value!!.constValue(program)!! - errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position) - } else { - // don't make it signed if it was unsigned and vice versa, except when it is a long const declaration - if(!decl.datatype.isLong && - (valueDt.isSigned && decl.datatype.isUnsigned || - valueDt.isUnsigned && decl.datatype.isSigned)) - { + VarDeclType.CONST -> { + // change the vardecl type itself as well, but only if new type is smaller + if(valueDt.largerSizeThan(decl.datatype)) { val constValue = decl.value!!.constValue(program)!! errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position) } else { - val changed = decl.copy(valueDt) - return listOf(IAstModification.ReplaceNode(decl, changed, parent)) + // don't make it signed if it was unsigned and vice versa, except when it is a long const declaration + if(!decl.datatype.isLong && + (valueDt.isSigned && decl.datatype.isUnsigned || + valueDt.isUnsigned && decl.datatype.isSigned)) + { + val constValue = decl.value!!.constValue(program)!! + errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position) + } else { + val changed = decl.copy(valueDt) + return listOf(IAstModification.ReplaceNode(decl, changed, parent)) + } } } + VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes) + throw FatalAstException("value type for a memory var should be word or byte (address)") } - VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes) - throw FatalAstException("value type for a memory var should be word or byte (address)") } - } // check splitting of word arrays diff --git a/compiler/test/TestPointers.kt b/compiler/test/TestPointers.kt index 806b1f196..95cb56e6c 100644 --- a/compiler/test/TestPointers.kt +++ b/compiler/test/TestPointers.kt @@ -690,20 +690,28 @@ main { this.text = next as uword ; TODO fix type error; the cast should succeed } }""" - compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null + compileText(VMTarget(), false, src, outputDir) shouldNotBe null + compileText(C64Target(), false, src, outputDir) shouldNotBe null } - xtest("const pointer") { + test("const pointer gives proper error") { val src=""" main { sub start() { ^^uword @shared ptr = 7000 - const ^^uword cptr = 8000 ; TODO fix type error; loses pointer - ptr^^ = 12345 - cptr^^ = 12345 + const ^^uword cptr = 8000 + + ;ptr^^ = 12345 + ;cptr^^ = 12345 + + cx16.r0 = cptr + cx16.r1 = ptr } }""" - compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null + val errors = ErrorReporterForTests() + compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = true) shouldBe null + errors.errors.size shouldBe 1 + errors.errors[0] shouldContain("const can only be used on numbers and booleans") } test("unknown field") { diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index fe8fbc1e7..71f5daefa 100644 --- a/compiler/test/TestSymbolTable.kt +++ b/compiler/test/TestSymbolTable.kt @@ -208,8 +208,8 @@ private fun makeSt(): SymbolTable { val sub12 = StNode("sub2", StNodeType.SUBROUTINE, astSub2) block1.add(sub11) block1.add(sub12) - block1.add(StConstant("c1", BaseDataType.UWORD, 12345.0, astConstant1)) - block1.add(StConstant("blockc", BaseDataType.UWORD, 999.0, astConstant2)) + block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1)) + block1.add(StConstant("blockc", DataType.UWORD, 999.0, astConstant2)) sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v1)) sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v2)) sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3)) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4cabca954..cb04fd733 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -13,6 +13,7 @@ Future Things and Ideas - 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 const pointers (simple and struct types) - 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) diff --git a/examples/test.p8 b/examples/test.p8 index e45616dce..b8201e28c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,135 +1,26 @@ -;%zeropage basicsafe -;%import textio -; -;main { -; sub start() { -; ^^uword @shared ptr = 7000 -; const ^^uword cptr = 8000 ; TODO fix type error; loses pointer -; ptr^^ = 12345 -; cptr^^ = 12345 -; txt.print_uw(peekw(7000)) -; txt.spc() -; txt.print_uw(peekw(8000)) -; } -;} - - -; TYPE CAST CRASH: -;%zeropage basicsafe -;%import strings -;%import textio -; -;main { -; struct Line { -; ^^Line prev -; ^^Line next -; ^^ubyte text -; } -; uword buffer = memory("buffer", 100*sizeof(Line), 1) -; ^^Line next = buffer -; -; sub start() { -; ^^Line line = next -; next += 1 -; line.text = next as ^^ubyte ; TODO fix crash here -; next = (next as uword) + 81 -; txt.print_uwhex(buffer, true) txt.nl() -; txt.print_uwhex(next, true) txt.nl() -; txt.print_uwhex(line, true) txt.nl() -; txt.print_uwhex(line.text, true) txt.nl() -; } -;} - - -%import floats -%import textio -%zeropage basicsafe - main { + struct Node1 { + ubyte type + word ww + } + struct Node2 { + ubyte type + ^^ubyte text + } + sub start() { - bytetest() - wordtest() - longtest() - floattest() ; TODO fix invalid 6502 code gen /crash - } + ^^Node1 @shared next + ^^Node2 @shared this + ^^ubyte ubptr - sub bytetest() { - ubyte[] foo = [11, 22, 33] - ubyte i - txt.print("before:") - for i in 0 to 2 { - txt.chrout(' ') - txt.print_ub(foo[i]) - } - txt.nl() - foo[2] = foo[1] - foo[1] = foo[0] - foo[0] = 0 - txt.print(" after:") - for i in 0 to 2 { - txt.chrout(' ') - txt.print_ub(foo[i]) - } - txt.nl() - } + ubptr = next as ^^ubyte + ubptr = 12345 + ubptr = cx16.r0 + ubptr = next as uword ; TODO fix type error; the cast should succeed - sub wordtest() { - uword[] foo = [1111, 2222, 3333] - ubyte i - txt.print("before:") - for i in 0 to 2 { - txt.chrout(' ') - txt.print_uw(foo[i]) - } - txt.nl() - foo[2] = foo[1] - foo[1] = foo[0] - foo[0] = 0 - txt.print(" after:") - for i in 0 to 2 { - txt.chrout(' ') - txt.print_uw(foo[i]) - } - txt.nl() - } - - sub longtest() { - long[] foo = [111111, 222222, 333333] - ubyte i - txt.print("before:") - for i in 0 to 2 { - txt.chrout(' ') - txt.print_l(foo[i]) - } - txt.nl() - foo[2] = foo[1] - foo[1] = foo[0] - foo[0] = 0 - txt.print(" after:") - for i in 0 to 2 { - txt.chrout(' ') - txt.print_l(foo[i]) - } - txt.nl() - } - - sub floattest() { - float[] foo = [1.1, 2.2, 3.3] - ubyte i - txt.print("before:") - for i in 0 to 2 { - txt.chrout(' ') - txt.print_f(foo[i]) - } - txt.nl() - foo[2] = foo[1] - foo[1] = foo[0] - foo[0] = 0 - txt.print(" after:") - for i in 0 to 2 { - txt.chrout(' ') - txt.print_f(foo[i]) - } - txt.nl() + this.text = next as ^^ubyte + this.text = 12345 + this.text = cx16.r0 + this.text = next as uword ; TODO fix type error; the cast should succeed } } diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 45e651c22..978c8f6a0 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -270,6 +270,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { val value: String = when { dt.isBool -> constant.value.toInt().toString() dt.isFloat -> constant.value.toString() + dt.isPointer -> TODO("constant pointer $constant") dt.isInteger -> constant.value.toInt().toHex() else -> throw InternalCompilerException("weird dt") } diff --git a/simpleAst/src/prog8/code/SymbolTable.kt b/simpleAst/src/prog8/code/SymbolTable.kt index 13b138fc3..6c60062d1 100644 --- a/simpleAst/src/prog8/code/SymbolTable.kt +++ b/simpleAst/src/prog8/code/SymbolTable.kt @@ -267,7 +267,7 @@ class StStaticVariable(name: String, } -class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode?) : +class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode?) : StNode(name, StNodeType.CONSTANT, astNode) diff --git a/simpleAst/src/prog8/code/SymbolTableMaker.kt b/simpleAst/src/prog8/code/SymbolTableMaker.kt index 0eccf5eca..eabbf5f1e 100644 --- a/simpleAst/src/prog8/code/SymbolTableMaker.kt +++ b/simpleAst/src/prog8/code/SymbolTableMaker.kt @@ -48,7 +48,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp } is PtConstant -> { require(node.type.isNumericOrBool) - StConstant(node.name, node.type.base, node.value, node) + StConstant(node.name, node.type, node.value, node) } is PtLabel -> { StNode(node.name, StNodeType.LABEL, node)