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

@@ -107,7 +107,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
TODO("expression: indexing non-pointer field ${idxderef.variable}")
}
TODO("evaluate address of pointer dereference ${idxderef.position}")
TODO("expression: evaluate address of pointer dereference ${idxderef.position}")
val pointerReg = -1 // pointerTr.resultReg
val constIndex = idxderef.index.asConstInteger()

View File

@@ -113,7 +113,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
private fun makeFullyScoped(identifier: IdentifierReference) {
identifier.targetStatement(program)?.let { target ->
identifier.targetStatement()?.let { target ->
val scoped = (target as INamedStatement).scopedName
val scopedIdent = IdentifierReference(scoped, identifier.position)
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
@@ -149,7 +149,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
when (it) {
is NumericLiteral -> it.copy()
is IdentifierReference -> {
val target = it.targetStatement(program) ?: return emptyList()
val target = it.targetStatement() ?: return emptyList()
val scoped = (target as INamedStatement).scopedName
IdentifierReference(scoped, it.position)
}
@@ -205,7 +205,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
val sub = functionCallStatement.target.targetStatement(program.builtinFunctions) as? Subroutine
return if(sub==null || !canInline(sub, functionCallStatement))
noModifications
else
@@ -213,7 +213,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
val sub = functionCallExpr.target.targetStatement(program.builtinFunctions) as? Subroutine
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
"invalid inline sub at ${sub.position}"
@@ -249,7 +249,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
val stmt = sub.statements.single()
if (stmt is IFunctionCall) {
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
return existing !is VarDecl
return existing !is VarDecl && existing !is StructFieldRef
}
}
return true

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>()
}
})

View File

@@ -155,20 +155,29 @@ interface INameScope: IStatementContainer, INamedStatement {
}
private fun lookupQualified(scopedName: List<String>): Statement? {
// a scoped name refers to a name in another namespace, and always starts from the root.
val localSymbol = this.searchSymbol(scopedName[0])
if(localSymbol is VarDecl && localSymbol.datatype.isPointer) {
var struct = localSymbol.datatype.subType as? StructDecl
if(struct!=null) {
for ((idx, field) in scopedName.drop(1).withIndex()) {
val fieldDt = struct!!.getFieldType(field)
if (fieldDt == null)
break
if (idx == scopedName.size - 2) {
// was last path element
val pointer = IdentifierReference(scopedName, Position.DUMMY)
val ref = StructFieldRef(pointer, struct, fieldDt, field, Position.DUMMY)
ref.linkParents(this as Node)
return ref
}
struct = fieldDt.subType as? StructDecl
if(struct==null)
break
}
}
}
// TODO experimental code to be able to alias blocks too:
// val stmt = this.lookup(listOf(scopedName[0])) ?: return null
// if(stmt is Alias) {
// val block = this.lookup(stmt.target.nameInSource) ?: return null
// var statement = block as Statement?
// for(name in scopedName.drop(1)) {
// statement = (statement as? IStatementContainer)?.searchSymbol(name)
// if(statement==null)
// return null
// }
// return statement
// }
// a scoped name refers to a name in another namespace, and always starts from the root.
for(module in (this as Node).definingModule.program.modules) {
val block = module.searchSymbol(scopedName[0])
if(block!=null) {

View File

@@ -413,7 +413,7 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
override fun referencesIdentifier(nameInSource: List<String>) = arrayvar.referencesIdentifier(nameInSource) || indexer.referencesIdentifier(nameInSource)
override fun inferType(program: Program): InferredTypes.InferredType {
val target = arrayvar.targetStatement(program)
val target = arrayvar.targetStatement(program.builtinFunctions)
if (target is VarDecl) {
return when {
target.datatype.isString || target.datatype.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
@@ -527,7 +527,7 @@ data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: Array
override fun constValue(program: Program): NumericLiteral? {
if(msb)
return null
val target = this.identifier?.targetStatement(program)
val target = this.identifier?.targetStatement()
val targetVar = target as? VarDecl
if(targetVar!=null) {
if (targetVar.type == VarDeclType.MEMORY || targetVar.type == VarDeclType.CONST) {
@@ -1274,18 +1274,18 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
override val isSimple = true
fun targetStatement(program: Program?): Statement? =
if(program!=null && nameInSource.singleOrNull() in program.builtinFunctions.names)
fun targetStatement(builtins: IBuiltinFunctions? = null): Statement? =
if(builtins!=null && nameInSource.singleOrNull() in builtins.names)
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
else
definingScope.lookup(nameInSource)
fun targetVarDecl(): VarDecl? = targetStatement(null) as? VarDecl
fun targetSubroutine(): Subroutine? = targetStatement(null) as? Subroutine
fun targetStructDecl(): StructDecl? = targetStatement(null) as? StructDecl
fun targetVarDecl(): VarDecl? = targetStatement() as? VarDecl
fun targetSubroutine(): Subroutine? = targetStatement() as? Subroutine
fun targetStructDecl(): StructDecl? = targetStatement() as? StructDecl
fun targetStructFieldRef(): StructFieldRef? = targetStatement() as? StructFieldRef
fun targetNameAndType(program: Program): Pair<String, DataType> {
val target = targetStatement(program) as? INamedStatement ?: throw FatalAstException("can't find target for $nameInSource")
val target = targetStatement(program.builtinFunctions) as? INamedStatement ?: throw FatalAstException("can't find target for $nameInSource")
val targetname: String = if(target.name in program.builtinFunctions.names)
"<builtin>.${target.name}"
else
@@ -1313,6 +1313,10 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
}
return null
}
if(node is StructFieldRef)
return null
val vardecl = node as? VarDecl
if(vardecl==null) {
return null
@@ -1341,7 +1345,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
override fun referencesIdentifier(nameInSource: List<String>): Boolean = this.nameInSource==nameInSource
override fun inferType(program: Program): InferredTypes.InferredType {
return when (val targetStmt = targetStatement(program)) {
return when (val targetStmt = targetStatement(program.builtinFunctions)) {
is VarDecl -> {
if(targetStmt.datatype.isUndefined)
InferredTypes.unknown()
@@ -1361,6 +1365,9 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
is StructDecl -> {
InferredTypes.unknown() // the type of a structdecl itself is actually not defined
}
is StructFieldRef -> {
InferredTypes.knownFor(targetStmt.type)
}
else -> InferredTypes.unknown()
}
}
@@ -1462,7 +1469,7 @@ class FunctionCallExpression(override var target: IdentifierReference,
val constVal = constValue(program ,false)
if(constVal!=null)
return InferredTypes.knownFor(constVal.type)
val stmt = target.targetStatement(program) ?: return InferredTypes.unknown()
val stmt = target.targetStatement(program.builtinFunctions) ?: return InferredTypes.unknown()
when (stmt) {
is BuiltinFunctionPlaceholder -> {
return program.builtinFunctions.returnType(target.nameInSource[0])
@@ -1479,6 +1486,7 @@ class FunctionCallExpression(override var target: IdentifierReference,
// calling a struct is syntax for allocating a static instance, and returns a pointer to that (not the instance itself)
return InferredTypes.knownFor(DataType.pointer(stmt))
}
is StructFieldRef -> throw FatalAstException("cannot call a struct field $stmt")
else -> return InferredTypes.unknown()
}
}
@@ -1662,9 +1670,12 @@ class PtrDereference(val identifier: IdentifierReference, val chain: List<String
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun inferType(program: Program): InferredTypes.InferredType {
val first = identifier.targetStatement(program)
val first = identifier.targetStatement()
if(first==null)
return InferredTypes.unknown()
if(first is StructFieldRef) {
return InferredTypes.knownFor(first.type)
}
val vardecl = identifier.targetVarDecl()
if(vardecl==null || vardecl.datatype.isUndefined || (!vardecl.datatype.isPointer && !vardecl.datatype.isStructInstance) )
return InferredTypes.unknown()

View File

@@ -414,9 +414,12 @@ class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, v
override fun linkParents(parent: Node) {
this.parent = parent
// pointer and struct are not our property!
pointer.linkParents(this)
}
override val scopedName: List<String>
get() = pointer.nameInSource
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun referencesIdentifier(nameInSource: List<String>) = pointer.referencesIdentifier(nameInSource) || struct.referencesIdentifier(nameInSource)
@@ -629,6 +632,7 @@ data class AssignTarget(
is PtrIndexedDereference -> pointerIndexedDeref = replacement
is ArrayIndexedExpression -> arrayindexed = replacement
is DirectMemoryWrite -> memoryAddress = replacement
is PtrDereference -> pointerDereference = replacement
else -> throw FatalAstException("invalid replacement for AssignTarget.arrayindexed: $replacement")
}
}
@@ -662,6 +666,7 @@ data class AssignTarget(
if (identifier != null) {
val symbol = definingScope.lookup(identifier!!.nameInSource) ?: return InferredTypes.unknown()
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
if (symbol is StructFieldRef) return InferredTypes.knownFor(symbol.type)
}
return when {
arrayindexed != null -> arrayindexed!!.inferType(program)

View File

@@ -117,7 +117,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
override fun visit(identifier: IdentifierReference) {
val target = identifier.targetStatement(program)
val target = identifier.targetStatement(program.builtinFunctions)
if(target!=null) {
allIdentifiersAndTargets.add(identifier to target)
if(target is Subroutine)
@@ -135,6 +135,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
else if(scopeTarget is VarDecl) {
allIdentifiersAndTargets.add(identifier to scopeTarget)
break
} else if(scopeTarget is StructFieldRef) {
TODO("register struct field ref in callgraph? $scopeTarget")
}
}
}

View File

@@ -54,6 +54,7 @@ STRUCTS and TYPED POINTERS
- DONE: what about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not constants.
- DONE: make typeForAddressOf() be even more specific about the typed pointers it returns for the address-of operator.
- fix ptr indexed problems (see todo.p8)
- Can we now get rid of PtPointerIndexedDeref ? Both for expression (value) as assigntarget? All code for translate(idxderef: PtPointerIndexedDeref) in ExpressionGen?
- add unit tests for all changes (pointers and structs)
- 6502 codegen: remove checks in checkForPointerTypesOn6502()
- 6502 codegen should warn about writing to initialized struct instances when using romable code, like with arrays "can only be used as read-only in ROMable code"

View File

@@ -5,10 +5,8 @@ main {
}
sub start() {
^^List l1 = List()
cx16.r0= l1.s[2]
l1.s[2] = cx16.r1L
; l1.s^^ = 2 TODO fix undefined symbol
cx16.r1 = l1.s^^ ; TODO fix "undefined symbol" error (and fix the unit test too)
}
}
; TODO also fix the compilation error in re.p8

View File

@@ -412,6 +412,10 @@ class PtPointerDeref(type: DataType, val chain: List<String>, val field: String?
// the start of the chain is the only child node (PtExpression that yields a pointer)
val startpointer: PtExpression
get() = children.single() as PtExpression
init {
require(!type.isUndefined)
}
}
class PtPointerIndexedDeref(type: DataType, position: Position) : PtExpression(type, position) {
@@ -419,6 +423,10 @@ class PtPointerIndexedDeref(type: DataType, position: Position) : PtExpression(t
get() = children[0] as PtIdentifier
val index: PtExpression
get() = children[1] as PtExpression
init {
require(!type.isUndefined)
}
}
// special node that isn't created from compiling user code, but used internally in the Intermediate Code