fix many ptr deref errors

This commit is contained in:
Irmen de Jong
2025-05-19 01:32:52 +02:00
parent adf5600a9b
commit f0b791452e
23 changed files with 203 additions and 83 deletions

View File

@@ -102,7 +102,7 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
if(args[0] is NumericLiteral)
return NumericLiteral.optimalInteger(program.memsizer.memorySize(dt.getOrUndef(), null), position)
val target = (args[0] as IdentifierReference).targetStatement(program)
val target = (args[0] as IdentifierReference).targetStatement()
?: throw CannotEvaluateException("sizeof", "no target")
return when {

View File

@@ -81,7 +81,7 @@ internal class AstChecker(private val program: Program,
}
checkLongType(identifier)
val stmt = identifier.targetStatement(program)
val stmt = identifier.targetStatement(program.builtinFunctions)
if(stmt==null) {
if(identifier.parent is ArrayIndexedExpression) {
// might be a pointer dereference chain
@@ -106,7 +106,7 @@ internal class AstChecker(private val program: Program,
if(identifier.nameInSource.size>1) {
val lookupModule = identifier.definingScope.lookup(identifier.nameInSource.take(1))
if(lookupModule is VarDecl) {
if(lookupModule is VarDecl && !lookupModule.datatype.isPointer) {
errors.err("ambiguous symbol name, block name expected but found variable", identifier.position)
}
}
@@ -340,7 +340,7 @@ internal class AstChecker(private val program: Program,
is InlineAssembly,
is IStatementContainer -> true
is Assignment -> {
val target = statement.target.identifier!!.targetStatement(program)
val target = statement.target.identifier!!.targetStatement()
target === statement.previousSibling() // an initializer assignment is okay
}
else -> false
@@ -378,7 +378,7 @@ internal class AstChecker(private val program: Program,
count++
}
override fun visit(jump: Jump) {
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement(program)
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement()
if(jumpTarget!=null) {
val sub = jump.definingSubroutine
val targetSub = jumpTarget as? Subroutine ?: jumpTarget.definingSubroutine
@@ -613,8 +613,8 @@ internal class AstChecker(private val program: Program,
val ident = repeatLoop.iterations as? IdentifierReference
if(ident!=null) {
val targetVar = ident.targetVarDecl()
if(targetVar==null)
val target = ident.targetStatement()
if(target !is VarDecl && target !is StructFieldRef)
errors.err("invalid assignment value", ident.position)
}
super.visit(repeatLoop)
@@ -627,7 +627,7 @@ internal class AstChecker(private val program: Program,
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt) && !targetDt.isIterable) {
if(!(valueDt issimpletype BaseDataType.STR && targetDt issimpletype BaseDataType.UWORD)) {
if(targetDt.isUnknown) {
if(assignment.target.identifier?.targetStatement(program)!=null)
if(assignment.target.identifier?.targetStatement(program.builtinFunctions)!=null)
errors.err("target datatype is unknown", assignment.target.position)
// otherwise, another error about missing symbol is already reported.
}
@@ -734,6 +734,9 @@ internal class AstChecker(private val program: Program,
errors.undefined(targetIdentifier.nameInSource, targetIdentifier.position)
return
}
is StructFieldRef -> {
// all is well
}
!is VarDecl -> {
errors.err("assignment LHS must be register or variable", assignment.position)
return
@@ -1297,7 +1300,7 @@ internal class AstChecker(private val program: Program,
count++
}
override fun visit(jump: Jump) {
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement(program)
val jumpTarget = (jump.target as? IdentifierReference)?.targetStatement()
if(jumpTarget!=null) {
val sub = jump.definingSubroutine
val targetSub = jumpTarget as? Subroutine ?: jumpTarget.definingSubroutine
@@ -1727,7 +1730,7 @@ internal class AstChecker(private val program: Program,
if(args[0] is AddressOf)
errors.err("can't call this indirectly, just use normal function call syntax", args[0].position)
else if(args[0] is IdentifierReference) {
val callTarget = (args[0] as IdentifierReference).targetStatement(program)
val callTarget = (args[0] as IdentifierReference).targetStatement(program.builtinFunctions)
if(callTarget !is VarDecl)
errors.err("can't call this indirectly, just use normal function call syntax", args[0].position)
}
@@ -1841,32 +1844,41 @@ internal class AstChecker(private val program: Program,
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
checkLongType(arrayIndexedExpression)
val target = arrayIndexedExpression.arrayvar.targetStatement(program)
val target = arrayIndexedExpression.arrayvar.targetStatement(program.builtinFunctions)
if(target is VarDecl) {
if(!target.datatype.isIterable && !target.datatype.isUnsignedWord && !target.datatype.isPointer)
errors.err("indexing requires an iterable, address uword, or pointer variable", arrayIndexedExpression.position)
if (!target.datatype.isIterable && !target.datatype.isUnsignedWord && !target.datatype.isPointer)
errors.err(
"indexing requires an iterable, address uword, or pointer variable",
arrayIndexedExpression.position
)
val indexVariable = arrayIndexedExpression.indexer.indexExpr as? IdentifierReference
if(indexVariable!=null) {
if(indexVariable.targetVarDecl()?.datatype?.isSigned==true) {
errors.err("variable array indexing can't be performed with signed variables", indexVariable.position)
if (indexVariable != null) {
if (indexVariable.targetVarDecl()?.datatype?.isSigned == true) {
errors.err(
"variable array indexing can't be performed with signed variables",
indexVariable.position
)
return
}
}
val arraysize = target.arraysize?.constIndex()
val index = arrayIndexedExpression.indexer.constIndex()
if(arraysize!=null) {
if(index!=null && (index<0 || index>=arraysize))
if (arraysize != null) {
if (index != null && (index < 0 || index >= arraysize))
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
} else if(target.datatype.isString) {
if(target.value is StringLiteral) {
} else if (target.datatype.isString) {
if (target.value is StringLiteral) {
// check string lengths for non-memory mapped strings
val stringLen = (target.value as StringLiteral).value.length
if (index != null && (index < 0 || index >= stringLen))
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
} else if(index!=null && index<0) {
} else if (index != null && index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
} else if(target is StructFieldRef) {
if(!target.type.isPointer && !target.type.isUnsignedWord)
errors.err("cannot array index on this field type", arrayIndexedExpression.indexer.position)
} else {
val parentExpr = arrayIndexedExpression.parent
if(parentExpr is BinaryExpression) {
@@ -2036,7 +2048,6 @@ internal class AstChecker(private val program: Program,
}
override fun visit(deref: PtrDereference) {
// unfortunately the AST regarding pointer dereferencing is a bit of a mess, and we cannot do precise type checking on elements inside such expressions yet.
if(deref.inferType(program).isUnknown)
errors.err("unable to determine type of dereferenced pointer expression", deref.position)
}

View File

@@ -215,7 +215,7 @@ internal fun Subroutine.hasRtsInAsm(checkOnlyLastInstruction: Boolean): Boolean
}
internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, statement: Statement, errors: IErrorReporter): Statement? {
when (val targetStatement = this.targetStatement(program)) {
when (val targetStatement = targetStatement(program.builtinFunctions)) {
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
is VarDecl -> {
if(statement is Jump) {
@@ -227,7 +227,7 @@ internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, st
else
errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
}
is Alias, is StructDecl -> {
is Alias, is StructDecl, is StructFieldRef -> {
return targetStatement
}
null -> {

View File

@@ -41,7 +41,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
}
override fun visit(alias: Alias) {
if(alias.target.targetStatement(program)==null)
if(alias.target.targetStatement(program.builtinFunctions)==null)
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
}
@@ -166,7 +166,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
}
override fun visit(deref: PtrDereference) {
val first = deref.identifier.targetStatement(program)
val first = deref.identifier.targetStatement()
if(first==null)
errors.undefined(deref.identifier.nameInSource, deref.identifier.position)
@@ -206,13 +206,13 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
private fun visitFunctionCall(call: IFunctionCall) {
if(call.target.nameInSource==listOf("rnd") || call.target.nameInSource==listOf("rndw")) {
val target = call.target.targetStatement(program)
val target = call.target.targetStatement(program.builtinFunctions)
if(target==null) {
errors.err("rnd() and rndw() builtin functions have been moved into the math module", call.position)
return
}
}
when (val target = call.target.targetStatement(program)) {
when (val target = call.target.targetStatement(program.builtinFunctions)) {
is Subroutine -> {
val expectedNumberOfArgs: Int = target.parameters.size
if(call.args.size != expectedNumberOfArgs) {
@@ -252,7 +252,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
if(target.type!=VarDeclType.VAR || !target.datatype.isUnsignedWord)
errors.err("wrong address variable datatype, expected uword", call.target.position)
}
is Alias, is StructDecl -> {}
is Alias, is StructDecl, is StructFieldRef -> {}
null -> {}
else -> errors.err("cannot call this as a subroutine or function", call.target.position)
}

View File

@@ -310,7 +310,7 @@ class AstPreprocessor(val program: Program,
}
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
val tgt = alias.target.targetStatement(program)
val tgt = alias.target.targetStatement(program.builtinFunctions)
if(tgt is Block) {
errors.err("cannot alias blocks", alias.target.position)
}

View File

@@ -399,7 +399,7 @@ _after:
if(parent is PtrIndexedDereference || parent.parent is PtrIndexedDereference)
return noModifications
if(identifier.nameInSource.size>1 && identifier.targetStatement(program)==null) {
if(identifier.nameInSource.size>1 && identifier.targetStatement()==null) {
// the a.b.c.d could be a pointer dereference chain a^^.b^^^.c^^^.d
for(i in identifier.nameInSource.size-1 downTo 1) {
val symbol = identifier.definingScope.lookup(identifier.nameInSource.take(i)) as? VarDecl

View File

@@ -130,7 +130,7 @@ internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Pr
}
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
val target = alias.target.targetStatement(program)
val target = alias.target.targetStatement()
if(target is Alias) {
val newAlias = Alias(alias.alias, target.target, alias.position)
return listOf(IAstModification.ReplaceNode(alias, newAlias, parent))
@@ -139,11 +139,11 @@ internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Pr
}
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
val target = identifier.targetStatement(program)
val target = identifier.targetStatement()
// don't replace an identifier in an Alias or when the alias points to another alias (that will be resolved first elsewhere)
if(target is Alias && parent !is Alias) {
if(target.target.targetStatement(program) !is Alias)
if(target.target.targetStatement() !is Alias)
return listOf(IAstModification.ReplaceNode(identifier, target.target.copy(position = identifier.position), parent))
}
@@ -167,7 +167,7 @@ internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Pr
val rightIndex = expr.right as? ArrayIndexedExpression
if (leftIdent != null && rightIndex != null) {
// maybe recombine IDENTIFIER . ARRAY[IDX] --> COMBINEDIDENTIFIER[IDX]
val leftTarget = leftIdent.targetStatement(null)
val leftTarget = leftIdent.targetStatement()
if(leftTarget==null || leftTarget !is StructDecl) {
val combinedName = leftIdent.nameInSource + rightIndex.arrayvar.nameInSource
val combined = IdentifierReference(combinedName, leftIdent.position)

View File

@@ -121,9 +121,9 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
throw FatalAstException("unknown dt")
}
val start = transform(deref.identifier)
val deref = PtPointerDeref(type, deref.chain, deref.field,deref.position)
deref.add(start)
return deref
val result = PtPointerDeref(type, deref.chain, deref.field,deref.position)
result.add(start)
return result
}
private fun transform(ifExpr: IfExpression): PtIfExpression {

View File

@@ -282,7 +282,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
// see if a typecast is needed to convert the arguments into the required parameter type
val modifications = mutableListOf<IAstModification>()
val paramsPossibleDatatypes = when(val sub = call.target.targetStatement(program)) {
val paramsPossibleDatatypes = when(val sub = call.target.targetStatement(program.builtinFunctions)) {
is BuiltinFunctionPlaceholder -> {
BuiltinFunctions.getValue(sub.name).parameters.map {
it.possibleDatatypes.map { dt ->
@@ -465,7 +465,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
for((index, elt) in array.value.withIndex()) {
if (elt is IdentifierReference) {
val eltType = elt.inferType(program)
val tgt = elt.targetStatement(program)
val tgt = elt.targetStatement()
if(eltType.isIterable || tgt is Subroutine || tgt is Label || tgt is Block) {
val addressof = AddressOf(elt, null, null, false, elt.position)
addressof.linkParents(array)

View File

@@ -112,13 +112,13 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
if(firstUnknownDt>=0) {
// if an uword is expected but a pointer is provided, that is okay without a cast
val identifier = call.args[0] as? IdentifierReference
return if(identifier==null || identifier.targetStatement(program)!=null)
return if(identifier==null || identifier.targetStatement(program.builtinFunctions)!=null)
Pair("argument ${firstUnknownDt + 1} invalid argument type", call.args[firstUnknownDt].position)
else
null
}
val argtypes = argITypes.map { it.getOrUndef() }
val target = call.target.targetStatement(program)
val target = call.target.targetStatement(program.builtinFunctions)
if (target is Subroutine) {
val consideredParamTypes: List<DataType> = target.parameters.map { it.type }
if(argtypes.size != consideredParamTypes.size)

View File

@@ -7,6 +7,7 @@ import io.kotest.matchers.collections.shouldContain
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.maps.shouldNotContainKey
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.Program
import prog8.ast.statements.Block
@@ -312,4 +313,20 @@ xyz {
val blocks2 = result2.codegenAst!!.allBlocks().toList()
blocks2.any { it.name=="xyz" } shouldBe false
}
test("symbol lookup of pointer fields should mark variable as used in callgraph") {
val src = """
main {
struct List {
^^uword s
ubyte n
}
sub start() {
^^List l1 = List()
l1.s^^ = 2
}
}"""
compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
}
})

View File

@@ -62,7 +62,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
str0.definingScope.name shouldBe "main"
val id1 = (args[1] as AddressOf).identifier!!
val lbl1 = id1.targetStatement(program) as Label
val lbl1 = id1.targetStatement() as Label
lbl1.name shouldBe "foo_bar"
lbl1.definingScope.name shouldBe "main"
}

View File

@@ -7,6 +7,7 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.types.instanceOf
import prog8.ast.expressions.PtrDereference
import prog8.ast.statements.Assignment
import prog8.ast.statements.VarDecl
import prog8.code.ast.PtAssignment
@@ -363,4 +364,61 @@ main {
st[4] shouldBe instanceOf<Assignment>()
st[5] shouldBe instanceOf<Assignment>()
}
test("indexing pointers with index 0 is just a direct pointer dereference") {
val src="""
main {
struct List {
^^uword s
ubyte n
}
sub start() {
^^List l1 = List()
cx16.r0 = l1.s[0]
l1.s[0] = 4242
cx16.r1 = l1.s^^
^^word @shared wptr
cx16.r0s = wptr[0]
cx16.r1s = wptr^^
wptr[0] = 4242
}
}"""
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 10
val dr0 = (st[2] as Assignment).value as PtrDereference
val dr1 = (st[3] as Assignment).target.pointerDereference!!
val dr2 = (st[4] as Assignment).value as PtrDereference
val dr3 = (st[6] as Assignment).value as PtrDereference
val dr4 = (st[7] as Assignment).value as PtrDereference
val dr5 = (st[8] as Assignment).target.pointerDereference!!
dr0.identifier.nameInSource shouldBe listOf("l1", "s")
dr0.chain.size shouldBe 0
dr0.field shouldBe null
dr1.identifier.nameInSource shouldBe listOf("l1", "s")
dr1.chain.size shouldBe 0
dr1.field shouldBe null
dr2.identifier.nameInSource shouldBe listOf("l1", "s")
dr2.chain.size shouldBe 0
dr2.field shouldBe null
dr3.identifier.nameInSource shouldBe listOf("wptr")
dr3.chain.size shouldBe 0
dr3.field shouldBe null
dr4.identifier.nameInSource shouldBe listOf("wptr")
dr4.chain.size shouldBe 0
dr4.field shouldBe null
dr5.identifier.nameInSource shouldBe listOf("wptr")
dr5.chain.size shouldBe 0
dr5.field shouldBe null
}
})

View File

@@ -63,11 +63,11 @@ class TestIdentifierRef: FunSpec({
val mainref = ((stmts[1] as Assignment).value as AddressOf).identifier!!
wwref.nameInSource shouldBe listOf("ww")
wwref.wasStringLiteral() shouldBe false
wwref.targetStatement(program) shouldBe instanceOf<VarDecl>()
wwref.targetStatement() shouldBe instanceOf<VarDecl>()
wwref.targetVarDecl()!!.name shouldBe "ww"
wwref.targetVarDecl()!!.parent shouldBe instanceOf<Block>()
mainref.nameInSource shouldBe listOf("main")
mainref.wasStringLiteral() shouldBe false
mainref.targetStatement(program) shouldBe instanceOf<Block>()
mainref.targetStatement() shouldBe instanceOf<Block>()
}
})