don't crash on certain undefined symbols, give proper error instead

Also the error handlers in unit tests now de-duplicate messages just like the compiler itself does
This commit is contained in:
Irmen de Jong 2023-03-11 14:55:13 +01:00
parent 4600772e05
commit d76547ead4
12 changed files with 90 additions and 27 deletions

View File

@ -34,7 +34,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PtContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
is PtArray, is PtString -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is PtArray, is PtString -> throw AssemblyError("string/array literal value assignment should have been replaced by a variable")
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
else -> TODO("missing expression asmgen for $expression")

View File

@ -1628,7 +1628,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
""")
}
TargetStorageKind.MEMORY -> {
throw AssemblyError("no asm gen for assign wordvar $sourceName to memory ${target.memory}")
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
}
TargetStorageKind.ARRAY -> {
target.array!!
@ -2348,7 +2348,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
TargetStorageKind.MEMORY -> {
throw AssemblyError("no asm gen for assign word $word to memory ${target.memory}")
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
}
TargetStorageKind.ARRAY -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
@ -2737,7 +2737,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte at $address to array ${wordtarget.asmVarname}")
throw AssemblyError("no asm gen for assign memory byte at $address to word array ${wordtarget.asmVarname}")
}
TargetStorageKind.REGISTER -> when(wordtarget.register!!) {
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda ${address.toHex()}")
@ -2765,7 +2765,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${wordtarget.asmVarname} ")
throw AssemblyError("no asm gen for assign memory byte $identifier to word array ${wordtarget.asmVarname} ")
}
TargetStorageKind.REGISTER -> {
asmgen.loadByteFromPointerIntoA(identifier)

View File

@ -34,11 +34,15 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
val warnings = mutableListOf<String>()
override fun err(msg: String, position: Position) {
errors.add("${position.toClickableStr()} $msg")
val text = "${position.toClickableStr()} $msg"
if(text !in errors)
errors.add(text)
}
override fun warn(msg: String, position: Position) {
warnings.add("${position.toClickableStr()} $msg")
val text = "${position.toClickableStr()} $msg"
if(text !in warnings)
warnings.add(text)
}
override fun noErrors(): Boolean = errors.isEmpty()

View File

@ -32,11 +32,15 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
val warnings = mutableListOf<String>()
override fun err(msg: String, position: Position) {
errors.add("${position.toClickableStr()} $msg")
val text = "${position.toClickableStr()} $msg"
if(text !in errors)
errors.add(text)
}
override fun warn(msg: String, position: Position) {
warnings.add("${position.toClickableStr()} $msg")
val text = "${position.toClickableStr()} $msg"
if(text !in warnings)
warnings.add(text)
}
override fun noErrors(): Boolean = errors.isEmpty()

View File

@ -405,7 +405,7 @@ private fun createAssemblyAndAssemble(program: PtProgram,
else if (compilerOptions.compTarget.name == VMTarget.NAME)
VmCodeGen()
else
throw NotImplementedError("no asm generator for cpu ${compilerOptions.compTarget.machine.cpu}")
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}")
val stMaker = SymbolTableMaker(program, compilerOptions)
val symbolTable = stMaker.make()

View File

@ -60,11 +60,16 @@ internal class AstChecker(private val program: Program,
}
override fun visit(identifier: IdentifierReference) {
val target = identifier.targetVarDecl(program)
if(target != null && target.origin==VarDeclOrigin.SUBROUTINEPARAM) {
if(target.definingSubroutine!!.isAsmSubroutine) {
if(target.definingSubroutine!!.parameters.any { it.name == identifier.nameInSource.last() })
errors.err("cannot refer to parameter of asmsub by name", identifier.position)
val stmt = identifier.targetStatement(program)
if(stmt==null)
errors.err("undefined symbol: ${identifier.nameInSource.joinToString(".")}", identifier.position)
else {
val target = stmt as? VarDecl
if (target != null && target.origin == VarDeclOrigin.SUBROUTINEPARAM) {
if (target.definingSubroutine!!.isAsmSubroutine) {
if (target.definingSubroutine!!.parameters.any { it.name == identifier.nameInSource.last() })
errors.err("cannot refer to parameter of asmsub by name", identifier.position)
}
}
}
}
@ -1323,7 +1328,7 @@ internal class AstChecker(private val program: Program,
else
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
}
null -> errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", target.position)
null -> errors.err("undefined symbol: ${target.nameInSource.joinToString(".")}", target.position)
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
}
return null

View File

@ -13,7 +13,9 @@ import prog8.code.core.IErrorReporter
import prog8.code.core.Position
import prog8.code.target.VMTarget
/**
* This checks for naming conflicts, not for correct symbol references yet.
*/
internal class AstIdentifiersChecker(private val errors: IErrorReporter,
private val program: Program,
private val compTarget: ICompilationTarget

View File

@ -413,7 +413,16 @@ class TestScoping: FunSpec({
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
println(errors.errors)
/*
There are 4 errors and 3 warnings.
ERROR name conflict 'start', also defined...
ERROR name conflict 'var1Warn', also defined...
ERROR name conflict 'main', also defined...
ERROR name conflict 'internalOk', also defined...
WARN name 'var1Warn' shadows occurrence at...
WARN name 'var1Warn' shadows occurrence at...
WARN name 'var1Warn' shadows occurrence at...
*/
errors.warnings.size shouldBe 3
errors.warnings[0] shouldContain "var1Warn"
errors.warnings[0] shouldContain "shadows"
@ -424,7 +433,7 @@ class TestScoping: FunSpec({
errors.warnings[2] shouldContain "var1Warn"
errors.warnings[2] shouldContain "shadows"
errors.warnings[2] shouldContain "line 3"
errors.errors.size shouldBe 5
errors.errors.size shouldBe 4
errors.errors[0] shouldContain "name conflict"
errors.errors[0] shouldContain "start"
errors.errors[0] shouldContain "line 5"
@ -435,10 +444,7 @@ class TestScoping: FunSpec({
errors.errors[2] shouldContain "main"
errors.errors[2] shouldContain "line 2"
errors.errors[3] shouldContain "name conflict"
errors.errors[3] shouldContain "start"
errors.errors[3] shouldContain "line 5"
errors.errors[4] shouldContain "name conflict"
errors.errors[4] shouldContain "internalOk"
errors.errors[4] shouldContain "line 11"
errors.errors[3] shouldContain "internalOk"
errors.errors[3] shouldContain "line 11"
}
})

View File

@ -201,5 +201,17 @@ main {
value2.left shouldBe instanceOf<ContainmentCheck>()
(value2.right as NumericLiteral).number shouldBe 0.0
}
test("const pointer variable indexing works") {
val src="""
main {
sub start() {
const uword pointer=$1000
cx16.r0L = pointer[2]
}
}
"""
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
}
})

View File

@ -4,6 +4,7 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThan
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldStartWith
import io.kotest.matchers.types.instanceOf
import prog8.code.ast.PtArrayIndexer
@ -11,6 +12,7 @@ import prog8.code.ast.PtAssignment
import prog8.code.ast.PtVariable
import prog8.code.core.DataType
import prog8.code.target.C64Target
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
class TestVariousCodeGen: FunSpec({
@ -109,4 +111,29 @@ main {
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("assigning memory byte into array works") {
val text="""
main {
sub start() {
uword factor1
ubyte[3] @shared factor124
factor124[0] = @(factor1)
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("reading memory from unknown var gives proper error") {
val text="""
main {
sub start() {
cx16.r0L = @(doesnotexist)
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, writeAssembly = true, errors = errors)
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "undefined symbol: doesnotexist"
}
})

View File

@ -10,11 +10,15 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
val warnings = mutableListOf<String>()
override fun err(msg: String, position: Position) {
errors.add("${position.toClickableStr()} $msg")
val text = "${position.toClickableStr()} $msg"
if(text !in errors)
errors.add(text)
}
override fun warn(msg: String, position: Position) {
warnings.add("${position.toClickableStr()} $msg")
val text = "${position.toClickableStr()} $msg"
if(text !in warnings)
warnings.add(text)
}
override fun noErrors(): Boolean = errors.isEmpty()

View File

@ -3,7 +3,6 @@ TODO
For next minor release
^^^^^^^^^^^^^^^^^^^^^^
- fix Github issue with array problems https://github.com/irmen/prog8/issues/99
- fix IR/VM: animals.p8 example is borked, it jumps straight to a suggestion and then somehow doesn't print the animal name correctly in the first question, and exits after 1 animal instead of looping
this has happened after v8.7: caused by c21913a6 ir: keep order of children in block 22-11-2022