mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +00:00
allow const expression intermediate values to be 32 bits integers to avoid needless overflow errors.
This commit is contained in:
parent
2b227b43fe
commit
c609e982fe
@ -1,12 +1,13 @@
|
||||
package prog8.code.core
|
||||
|
||||
enum class DataType {
|
||||
UBYTE, // pass by value
|
||||
BYTE, // pass by value
|
||||
UWORD, // pass by value
|
||||
WORD, // pass by value
|
||||
FLOAT, // pass by value
|
||||
BOOL, // pass by value
|
||||
UBYTE, // pass by value 8 bits unsigned
|
||||
BYTE, // pass by value 8 bits signed
|
||||
UWORD, // pass by value 16 bits unsigned
|
||||
WORD, // pass by value 16 bits signed
|
||||
LONG, // pass by value 32 bits signed
|
||||
FLOAT, // pass by value machine dependent
|
||||
BOOL, // pass by value bit 0 of a 8 bit byte
|
||||
STR, // pass by reference
|
||||
ARRAY_UB, // pass by reference
|
||||
ARRAY_B, // pass by reference
|
||||
@ -23,11 +24,12 @@ enum class DataType {
|
||||
*/
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
when(this) {
|
||||
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, FLOAT)
|
||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT, BOOL)
|
||||
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
|
||||
UWORD -> targetType.oneOf(UWORD, FLOAT)
|
||||
WORD -> targetType.oneOf(WORD, FLOAT)
|
||||
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, LONG, FLOAT)
|
||||
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT, BOOL)
|
||||
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
|
||||
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
|
||||
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
|
||||
LONG -> targetType.oneOf(LONG, FLOAT)
|
||||
FLOAT -> targetType.oneOf(FLOAT)
|
||||
STR -> targetType.oneOf(STR, UWORD)
|
||||
in ArrayDatatypes -> targetType == this
|
||||
@ -41,7 +43,8 @@ enum class DataType {
|
||||
this == other -> false
|
||||
this in ByteDatatypes -> false
|
||||
this in WordDatatypes -> other in ByteDatatypes
|
||||
this== STR && other== UWORD || this== UWORD && other== STR -> false
|
||||
this == LONG -> other in ByteDatatypes+WordDatatypes
|
||||
this == STR && other == UWORD || this == UWORD && other == STR -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
@ -123,11 +126,11 @@ enum class BranchCondition {
|
||||
|
||||
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL)
|
||||
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
|
||||
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.BOOL)
|
||||
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
|
||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
|
||||
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
|
||||
val IntegerDatatypes = IntegerDatatypesNoBool + DataType.BOOL
|
||||
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val NumericDatatypes = NumericDatatypesNoBool + DataType.BOOL
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
|
||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
|
||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
|
||||
|
@ -62,6 +62,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(identifier: IdentifierReference) {
|
||||
checkLongType(identifier)
|
||||
val stmt = identifier.targetStatement(program)
|
||||
if(stmt==null)
|
||||
errors.undefined(identifier.nameInSource, identifier.position)
|
||||
@ -279,6 +280,10 @@ internal class AstChecker(private val program: Program,
|
||||
super.visit(label)
|
||||
}
|
||||
|
||||
override fun visit(numLiteral: NumericLiteral) {
|
||||
checkLongType(numLiteral)
|
||||
}
|
||||
|
||||
private fun hasReturnOrJumpOrRts(scope: IStatementContainer): Boolean {
|
||||
class Searcher: IAstVisitor
|
||||
{
|
||||
@ -528,6 +533,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(assignTarget: AssignTarget) {
|
||||
if(assignTarget.inferType(program).istype(DataType.LONG))
|
||||
errors.err("integer overflow", assignTarget.position)
|
||||
|
||||
super.visit(assignTarget)
|
||||
|
||||
val memAddr = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
|
||||
@ -577,6 +585,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
checkLongType(addressOf)
|
||||
val variable=addressOf.identifier.targetVarDecl(program)
|
||||
if(variable!=null && variable.type==VarDeclType.CONST)
|
||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||
@ -584,6 +593,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl) {
|
||||
if(decl.datatype==DataType.LONG)
|
||||
errors.err("integer overflow", decl.position)
|
||||
|
||||
fun err(msg: String) = errors.err(msg, decl.position)
|
||||
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
@ -915,6 +927,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(expr: PrefixExpression) {
|
||||
checkLongType(expr)
|
||||
val dt = expr.expression.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(dt==DataType.UNDEFINED)
|
||||
return // any error should be reported elsewhere
|
||||
@ -935,6 +948,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(expr: BinaryExpression) {
|
||||
super.visit(expr)
|
||||
checkLongType(expr)
|
||||
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
val rightIDt = expr.right.inferType(program)
|
||||
@ -1036,6 +1050,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(typecast: TypecastExpression) {
|
||||
checkLongType(typecast)
|
||||
if(typecast.type in IterableDatatypes)
|
||||
errors.err("cannot type cast to string or array type", typecast.position)
|
||||
|
||||
@ -1082,6 +1097,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) {
|
||||
checkLongType(functionCallExpr)
|
||||
// this function call is (part of) an expression, which should be in a statement somewhere.
|
||||
val stmtOfExpression = findParentNode<Statement>(functionCallExpr)
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
|
||||
@ -1137,6 +1153,11 @@ internal class AstChecker(private val program: Program,
|
||||
super.visit(functionCallExpr)
|
||||
}
|
||||
|
||||
override fun visit(bfc: BuiltinFunctionCall) {
|
||||
checkLongType(bfc)
|
||||
super.visit(bfc)
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val targetStatement = functionCallStatement.target.checkFunctionOrLabelExists(program, functionCallStatement, errors)
|
||||
if(targetStatement!=null) {
|
||||
@ -1273,6 +1294,10 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args.forEach{
|
||||
checkLongType(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||
@ -1310,6 +1335,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
checkLongType(arrayIndexedExpression)
|
||||
val target = arrayIndexedExpression.arrayvar.targetStatement(program)
|
||||
if(target is VarDecl) {
|
||||
if(target.datatype !in IterableDatatypes && target.datatype!=DataType.UWORD)
|
||||
@ -1454,6 +1480,12 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("%asm containing IR code cannot be translated to 6502 assembly", inlineAssembly.position)
|
||||
}
|
||||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
if(expression.inferType(program).istype(DataType.LONG)) {
|
||||
errors.err("integer overflow", expression.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteral) : Boolean {
|
||||
return if (targetDt == DataType.STR) {
|
||||
when {
|
||||
|
@ -1043,4 +1043,27 @@ main {
|
||||
errors.errors.single() shouldContain "doesn't match"
|
||||
}
|
||||
|
||||
test("long type okay in const expr but otherwise overflow") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
const ubyte HEIGHT=240
|
||||
uword large = 320*240/8/8
|
||||
thing(large)
|
||||
thing(320*240/8/8)
|
||||
thing(320*HEIGHT/8/8)
|
||||
thing(320*HEIGHT) ; overflow
|
||||
}
|
||||
|
||||
sub thing(uword value) {
|
||||
value++
|
||||
}
|
||||
}"""
|
||||
val errors=ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "can't cast"
|
||||
errors.errors[1] shouldContain "overflow"
|
||||
}
|
||||
|
||||
})
|
||||
|
@ -484,6 +484,7 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
||||
in -128..127 -> NumericLiteral(DataType.BYTE, dvalue, position)
|
||||
in 0..65535 -> NumericLiteral(DataType.UWORD, dvalue, position)
|
||||
in -32768..32767 -> NumericLiteral(DataType.WORD, dvalue, position)
|
||||
in -2147483647..2147483647 -> NumericLiteral(DataType.LONG, dvalue, position)
|
||||
else -> throw FatalAstException("integer overflow: $dvalue")
|
||||
}
|
||||
}
|
||||
@ -492,6 +493,7 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
||||
return when (value) {
|
||||
in 0u..255u -> NumericLiteral(DataType.UBYTE, value.toDouble(), position)
|
||||
in 0u..65535u -> NumericLiteral(DataType.UWORD, value.toDouble(), position)
|
||||
in 0u..2147483647u -> NumericLiteral(DataType.LONG, value.toDouble(), position)
|
||||
else -> throw FatalAstException("unsigned integer overflow: $value")
|
||||
}
|
||||
}
|
||||
@ -635,6 +637,9 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
||||
DataType.BOOL -> {
|
||||
return CastValue(true, NumericLiteral(targettype, number, position))
|
||||
}
|
||||
DataType.LONG -> {
|
||||
/* ignore this cast, LONG can't be used. Error will be given elsewhere */
|
||||
}
|
||||
else -> {
|
||||
throw FatalAstException("type cast of weird type $type")
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ object InferredTypes {
|
||||
DataType.BYTE to InferredType.known(DataType.BYTE),
|
||||
DataType.UWORD to InferredType.known(DataType.UWORD),
|
||||
DataType.WORD to InferredType.known(DataType.WORD),
|
||||
DataType.LONG to InferredType.known(DataType.LONG),
|
||||
DataType.FLOAT to InferredType.known(DataType.FLOAT),
|
||||
DataType.BOOL to InferredType.known(DataType.BOOL),
|
||||
DataType.STR to InferredType.known(DataType.STR),
|
||||
|
@ -12,7 +12,6 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- Currently "320*240/8/8" gives integer overflow, so: allow constant integer subexpressions to contain out of range integers (>65535 etc) as long as the final constant value is within byte/word range.
|
||||
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays.
|
||||
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
|
||||
(this is already done hardcoded for several of the builtin functions)
|
||||
|
@ -1,12 +1,14 @@
|
||||
%zeropage basicsafe
|
||||
%import textio
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.print(iso:"This is ISO text.\n")
|
||||
const ubyte HEIGHT=240
|
||||
uword large = 320*240/8/8
|
||||
thing(large)
|
||||
thing(320*240/8/8)
|
||||
thing(320*HEIGHT/8/8)
|
||||
thing(320*HEIGHT) ; overflow
|
||||
}
|
||||
|
||||
repeat {
|
||||
}
|
||||
sub thing(uword value) {
|
||||
value++
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user