mirror of
https://github.com/irmen/prog8.git
synced 2024-11-28 10:51:14 +00:00
setting a byte >=128 or word >=32768 now results in an out-of-range error, instead of an invalid casted value
This commit is contained in:
parent
4958463e75
commit
f569ce6141
@ -32,15 +32,9 @@ class VarConstantValueTypeAdjuster(
|
|||||||
val declConstValue = decl.value?.constValue(program)
|
val declConstValue = decl.value?.constValue(program)
|
||||||
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||||
&& declConstValue.type != decl.datatype) {
|
&& declConstValue.type != decl.datatype) {
|
||||||
// avoid silent float roundings
|
|
||||||
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
|
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
|
||||||
|
// avoid silent float roundings
|
||||||
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
|
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
|
||||||
} else if(decl.datatype!=DataType.BOOL) {
|
|
||||||
// cast the numeric literal to the appropriate datatype of the variable if it's not boolean
|
|
||||||
declConstValue.linkParents(decl)
|
|
||||||
val cast = declConstValue.cast(decl.datatype, true)
|
|
||||||
if (cast.isValid)
|
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (x: UndefinedSymbolError) {
|
} catch (x: UndefinedSymbolError) {
|
||||||
|
@ -50,15 +50,22 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
VarDeclType.CONST -> {
|
VarDeclType.CONST -> {
|
||||||
// change the vardecl type itself as well, but only if it's smaller
|
// change the vardecl type itself as well, but only if new type is smaller
|
||||||
if(valueDt.largerThan(decl.datatype)) {
|
if(valueDt.largerThan(decl.datatype)) {
|
||||||
val constValue = decl.value!!.constValue(program)!!
|
val constValue = decl.value!!.constValue(program)!!
|
||||||
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
||||||
|
} else {
|
||||||
|
// don't make it signed if it was unsigned and vice versa
|
||||||
|
if(valueDt in SignedDatatypes && decl.datatype !in SignedDatatypes ||
|
||||||
|
valueDt !in SignedDatatypes && decl.datatype in SignedDatatypes) {
|
||||||
|
val constValue = decl.value!!.constValue(program)!!
|
||||||
|
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
||||||
} else {
|
} else {
|
||||||
val changed = decl.copy(valueDt)
|
val changed = decl.copy(valueDt)
|
||||||
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
|
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes)
|
VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes)
|
||||||
throw FatalAstException("value type for a memory var should be word or byte (address)")
|
throw FatalAstException("value type for a memory var should be word or byte (address)")
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package prog8tests.ast
|
|||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import io.kotest.matchers.shouldNotBe
|
import io.kotest.matchers.shouldNotBe
|
||||||
|
import io.kotest.matchers.string.shouldContain
|
||||||
import io.kotest.matchers.types.instanceOf
|
import io.kotest.matchers.types.instanceOf
|
||||||
import prog8.ast.expressions.AddressOf
|
import prog8.ast.expressions.AddressOf
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
@ -16,6 +17,7 @@ import prog8.code.core.DataType
|
|||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.Cx16Target
|
import prog8.code.target.Cx16Target
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
|
|
||||||
class TestConst: FunSpec({
|
class TestConst: FunSpec({
|
||||||
@ -323,4 +325,82 @@ main {
|
|||||||
assignAddr2.identifier.nameInSource shouldBe listOf("buffer")
|
assignAddr2.identifier.nameInSource shouldBe listOf("buffer")
|
||||||
assignAddr2.arrayIndex!!.indexExpr shouldBe instanceOf<BinaryExpression>()
|
assignAddr2.arrayIndex!!.indexExpr shouldBe instanceOf<BinaryExpression>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("out of range const byte and word give correct error") {
|
||||||
|
var src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
const byte MIN_BYTE = -129
|
||||||
|
const word MIN_WORD = -32769
|
||||||
|
const byte MAX_BYTE = 128
|
||||||
|
const word MAX_WORD = 32768
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 4
|
||||||
|
errors.errors[0] shouldContain "out of range"
|
||||||
|
errors.errors[1] shouldContain "out of range"
|
||||||
|
errors.errors[2] shouldContain "out of range"
|
||||||
|
errors.errors[3] shouldContain "out of range"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("out of range var byte and word give correct error") {
|
||||||
|
var src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
byte @shared v_MIN_BYTE = -129
|
||||||
|
word @shared v_MIN_WORD = -32769
|
||||||
|
byte @shared v_MAX_BYTE = 128
|
||||||
|
word @shared v_MAX_WORD = 32768
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 8
|
||||||
|
errors.errors[0] shouldContain "out of range"
|
||||||
|
errors.errors[2] shouldContain "out of range"
|
||||||
|
errors.errors[4] shouldContain "out of range"
|
||||||
|
errors.errors[6] shouldContain "out of range"
|
||||||
|
}
|
||||||
|
|
||||||
|
test("out of range const byte and word no errors with explicit cast if possible") {
|
||||||
|
var src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
const byte MIN_BYTE = -129 as byte ; still error
|
||||||
|
const word MIN_WORD = -32769 as word ; still error
|
||||||
|
const byte MAX_BYTE = 128 as byte
|
||||||
|
const word MAX_WORD = 32768 as word
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 4
|
||||||
|
errors.errors[0] shouldContain(":4:31: const declaration needs a compile-time constant")
|
||||||
|
errors.errors[1] shouldContain(":4:32: no cast available")
|
||||||
|
errors.errors[2] shouldContain(":5:31: const declaration needs a compile-time constant")
|
||||||
|
errors.errors[3] shouldContain(":5:32: no cast available")
|
||||||
|
}
|
||||||
|
|
||||||
|
test("out of range var byte and word no errors with explicit cast if possible") {
|
||||||
|
var src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
byte @shared v_min_byte2 = -129 as byte ; still error
|
||||||
|
word @shared v_min_word2 = -32769 as word ; still error
|
||||||
|
byte @shared v_min_byte3 = 255 as byte
|
||||||
|
word @shared v_min_word3 = 50000 as word
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
|
||||||
|
errors.errors.size shouldBe 2
|
||||||
|
errors.errors[0] shouldContain(":4:37: no cast available")
|
||||||
|
errors.errors[1] shouldContain(":5:37: no cast available")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -629,7 +629,7 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
|||||||
|
|
||||||
when(type) {
|
when(type) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(targettype==DataType.BYTE)
|
if(targettype==DataType.BYTE && (number in 0.0..127.0 || !implicit))
|
||||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number.toInt().toByte().toDouble(), position))
|
return ValueAfterCast(true, null, NumericLiteral(targettype, number.toInt().toByte().toDouble(), position))
|
||||||
if(targettype==DataType.WORD || targettype==DataType.UWORD)
|
if(targettype==DataType.WORD || targettype==DataType.UWORD)
|
||||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||||
@ -667,7 +667,7 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
|||||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||||
if(targettype==DataType.UBYTE && number <= 255)
|
if(targettype==DataType.UBYTE && number <= 255)
|
||||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||||
if(targettype==DataType.WORD)
|
if(targettype==DataType.WORD && (number <= 32767 || !implicit))
|
||||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number.toInt().toShort().toDouble(), position))
|
return ValueAfterCast(true, null, NumericLiteral(targettype, number.toInt().toShort().toDouble(), position))
|
||||||
if(targettype==DataType.FLOAT)
|
if(targettype==DataType.FLOAT)
|
||||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
this should not be allowed:
|
|
||||||
const byte MAX_BYTE = 128 ; actual value ends up as -128
|
|
||||||
const word MAX_WORD = 32768 ; actual value ends up as -32768
|
|
||||||
(how are vars behaving? what if you explicitly cast - that should work?)
|
|
||||||
|
|
||||||
|
|
||||||
import is now order dependent due to merges, you must use this order:
|
import is now order dependent due to merges, you must use this order:
|
||||||
%import textio
|
%import textio
|
||||||
%import floats
|
%import floats
|
||||||
|
Loading…
Reference in New Issue
Block a user