mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 19:54:26 +00:00
better error handling of invalid number casts
This commit is contained in:
parent
749ad700d8
commit
17694c1d01
@ -25,7 +25,7 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
|
||||
&& declConstValue.type != decl.datatype) {
|
||||
// avoid silent float roundings
|
||||
if(decl.datatype in IntegerDatatypes && declConstValue.type==DataType.FLOAT) {
|
||||
errors.err("refused silent rounding of float to avoid loss of precision", decl.value!!.position)
|
||||
errors.err("refused rounding of float to avoid loss of precision", decl.value!!.position)
|
||||
} else {
|
||||
// cast the numeric literal to the appropriate datatype of the variable
|
||||
val cast = declConstValue.cast(decl.datatype)
|
||||
|
@ -485,19 +485,11 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
// target type check is already done at the Assignment:
|
||||
// val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR)
|
||||
// if(targetDt in IterableDatatypes)
|
||||
// errors.err("cannot assign to a string or array type", assignTarget.position)
|
||||
|
||||
if (assignment is Assignment) {
|
||||
|
||||
val targetDatatype = assignTarget.inferType(program)
|
||||
if (targetDatatype.isKnown) {
|
||||
val constVal = assignment.value.constValue(program)
|
||||
if (constVal != null) {
|
||||
checkValueTypeAndRange(targetDatatype.getOr(DataType.BYTE), constVal)
|
||||
} else {
|
||||
if(constVal==null) {
|
||||
val sourceDatatype = assignment.value.inferType(program)
|
||||
if (sourceDatatype.isUnknown) {
|
||||
if (assignment.value !is FunctionCallExpression)
|
||||
@ -897,6 +889,9 @@ internal class AstChecker(private val program: Program,
|
||||
if(!typecast.expression.inferType(program).isKnown)
|
||||
errors.err("this expression doesn't return a value", typecast.expression.position)
|
||||
|
||||
if(typecast.expression is NumericLiteralValue)
|
||||
errors.err("can't cast the value to the requested target type", typecast.expression.position)
|
||||
|
||||
super.visit(typecast)
|
||||
}
|
||||
|
||||
|
114
compiler/test/TestTypecasts.kt
Normal file
114
compiler/test/TestTypecasts.kt
Normal file
@ -0,0 +1,114 @@
|
||||
package prog8tests
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.statements.Pipe
|
||||
import prog8.codegen.target.C64Target
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.assertFailure
|
||||
import prog8tests.helpers.assertSuccess
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
|
||||
class TestTypecasts: FunSpec({
|
||||
|
||||
test("correct typecasts") {
|
||||
val text = """
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
float @shared fl = 3.456
|
||||
uword @shared uw = 5555
|
||||
byte @shared bb = -44
|
||||
|
||||
bb = uw as byte
|
||||
uw = bb as uword
|
||||
fl = uw as float
|
||||
fl = bb as float
|
||||
bb = fl as byte
|
||||
uw = fl as uword
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
|
||||
result.program.entrypoint.statements.size shouldBe 13
|
||||
}
|
||||
|
||||
test("invalid typecasts of numbers") {
|
||||
val text = """
|
||||
%import floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared bb
|
||||
|
||||
bb = 5555 as ubyte
|
||||
routine(5555 as ubyte)
|
||||
}
|
||||
|
||||
sub routine(ubyte bb) {
|
||||
bb++
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, writeAssembly = true, errors=errors).assertFailure()
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "can't cast"
|
||||
errors.errors[1] shouldContain "can't cast"
|
||||
}
|
||||
|
||||
test("refuse to round float literal 1") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
main {
|
||||
sub start() {
|
||||
float @shared fl = 3.456 as uword
|
||||
fl = 1.234 as uword
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, errors=errors).assertFailure()
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "can't cast"
|
||||
errors.errors[1] shouldContain "can't cast"
|
||||
}
|
||||
|
||||
test("refuse to round float literal 2") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
main {
|
||||
sub start() {
|
||||
float @shared fl = 3.456
|
||||
fl++
|
||||
fl = fl as uword
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, errors=errors).assertFailure()
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "in-place makes no sense"
|
||||
}
|
||||
|
||||
test("refuse to round float literal 3") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
main {
|
||||
sub start() {
|
||||
uword @shared ww = 3.456 as uword
|
||||
ww++
|
||||
ww = 3.456 as uword
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, errors=errors).assertFailure()
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "can't cast"
|
||||
errors.errors[1] shouldContain "can't cast"
|
||||
}
|
||||
})
|
@ -431,7 +431,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||
else {
|
||||
val rounded = round(numbervalue)
|
||||
if(rounded != numbervalue)
|
||||
throw ExpressionError("refused silent rounding of float to avoid loss of precision", position)
|
||||
throw ExpressionError("refused rounding of float to avoid loss of precision", position)
|
||||
rounded
|
||||
}
|
||||
}
|
||||
@ -569,14 +569,18 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||
return CastValue(true, NumericLiteralValue(targettype, number, position))
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if (targettype == DataType.BYTE && number >= -128 && number <=127)
|
||||
try {
|
||||
if (targettype == DataType.BYTE && number >= -128 && number <= 127)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number, position))
|
||||
if (targettype == DataType.UBYTE && number >=0 && number <= 255)
|
||||
if (targettype == DataType.UBYTE && number >= 0 && number <= 255)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number, position))
|
||||
if (targettype == DataType.WORD && number >= -32768 && number <= 32767)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number, position))
|
||||
if (targettype == DataType.UWORD && number >=0 && number <= 65535)
|
||||
if (targettype == DataType.UWORD && number >= 0 && number <= 65535)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number, position))
|
||||
} catch (x: ExpressionError) {
|
||||
return CastValue(false, null)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user