mirror of
https://github.com/irmen/prog8.git
synced 2025-11-02 13:16:07 +00:00
fix many ptr deref errors
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
@@ -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>()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user