mirror of
https://github.com/irmen/prog8.git
synced 2024-11-24 13:32:28 +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)
|
||||
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||
&& declConstValue.type != decl.datatype) {
|
||||
// avoid silent float roundings
|
||||
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)
|
||||
} 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) {
|
||||
|
@ -50,13 +50,20 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
val constValue = decl.value!!.constValue(program)!!
|
||||
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
||||
} else {
|
||||
val changed = decl.copy(valueDt)
|
||||
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
|
||||
// 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 {
|
||||
val changed = decl.copy(valueDt)
|
||||
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes)
|
||||
|
@ -3,6 +3,7 @@ package prog8tests.ast
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
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.AddressOf
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
@ -16,6 +17,7 @@ import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
class TestConst: FunSpec({
|
||||
@ -323,4 +325,82 @@ main {
|
||||
assignAddr2.identifier.nameInSource shouldBe listOf("buffer")
|
||||
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) {
|
||||
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))
|
||||
if(targettype==DataType.WORD || targettype==DataType.UWORD)
|
||||
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))
|
||||
if(targettype==DataType.UBYTE && number <= 255)
|
||||
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))
|
||||
if(targettype==DataType.FLOAT)
|
||||
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
|
||||
|
@ -1,12 +1,6 @@
|
||||
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 textio
|
||||
%import floats
|
||||
|
Loading…
Reference in New Issue
Block a user