mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 00:31:56 +00:00
improve automatic type conversions for return values, fixes #155
This commit is contained in:
parent
5731b79554
commit
ed09dd4e9e
@ -139,8 +139,7 @@ internal class AstChecker(private val program: Program,
|
||||
// you can return a string or array when an uword (pointer) is returned
|
||||
} else if(valueDt istype DataType.UWORD && expectedReturnValues[0]==DataType.STR) {
|
||||
// you can return a uword pointer when the return type is a string
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
|
||||
}
|
||||
}
|
||||
|
@ -308,6 +308,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(subroutine.returntypes.size==1) {
|
||||
val subReturnType = subroutine.returntypes.first()
|
||||
val returnDt = returnValue.inferType(program)
|
||||
if(returnDt isnot subReturnType && returnValue is NumericLiteral) {
|
||||
// see if we might change the returnvalue into the expected type
|
||||
val castedValue = returnValue.convertTypeKeepValue(subReturnType)
|
||||
if(castedValue.isValid) {
|
||||
return listOf(IAstModification.ReplaceNode(returnValue, castedValue.valueOrZero(), returnStmt))
|
||||
}
|
||||
}
|
||||
if (returnDt istype subReturnType or returnDt.isNotAssignableTo(subReturnType))
|
||||
return noModifications
|
||||
if (returnValue is NumericLiteral) {
|
||||
|
@ -10,6 +10,7 @@ import prog8.ast.expressions.ArrayLiteral
|
||||
import prog8.ast.expressions.InferredTypes
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.statements.AnonymousScope
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.Position
|
||||
@ -183,4 +184,41 @@ class TestNumericLiteral: FunSpec({
|
||||
NumericLiteral.optimalNumeric(-1234.0, Position.DUMMY).type shouldBe DataType.WORD
|
||||
NumericLiteral.optimalNumeric(-1234.0, Position.DUMMY).number shouldBe -1234.0
|
||||
}
|
||||
|
||||
test("cast can change value") {
|
||||
fun num(dt: DataType, num: Double): NumericLiteral {
|
||||
val n = NumericLiteral(dt, num, Position.DUMMY)
|
||||
n.linkParents(AnonymousScope(mutableListOf(), Position.DUMMY))
|
||||
return n
|
||||
}
|
||||
val cast1 = num(DataType.UBYTE, 200.0).cast(DataType.BYTE, false)
|
||||
cast1.isValid shouldBe true
|
||||
cast1.valueOrZero().number shouldBe -56.0
|
||||
val cast2 = num(DataType.BYTE, -50.0).cast(DataType.UBYTE, false)
|
||||
cast2.isValid shouldBe true
|
||||
cast2.valueOrZero().number shouldBe 206.0
|
||||
val cast3 = num(DataType.UWORD, 55555.0).cast(DataType.WORD, false)
|
||||
cast3.isValid shouldBe true
|
||||
cast3.valueOrZero().number shouldBe -9981.0
|
||||
val cast4 = num(DataType.WORD, -3333.0).cast(DataType.UWORD, false)
|
||||
cast4.isValid shouldBe true
|
||||
cast4.valueOrZero().number shouldBe 62203.0
|
||||
}
|
||||
|
||||
test("convert cannot change value") {
|
||||
fun num(dt: DataType, num: Double): NumericLiteral {
|
||||
val n = NumericLiteral(dt, num, Position.DUMMY)
|
||||
n.linkParents(AnonymousScope(mutableListOf(), Position.DUMMY))
|
||||
return n
|
||||
}
|
||||
num(DataType.UBYTE, 200.0).convertTypeKeepValue(DataType.BYTE).isValid shouldBe false
|
||||
num(DataType.BYTE, -50.0).convertTypeKeepValue(DataType.UBYTE).isValid shouldBe false
|
||||
num(DataType.UWORD, 55555.0).convertTypeKeepValue(DataType.WORD).isValid shouldBe false
|
||||
num(DataType.WORD, -3333.0).convertTypeKeepValue(DataType.UWORD).isValid shouldBe false
|
||||
|
||||
num(DataType.UBYTE, 42.0).convertTypeKeepValue(DataType.BYTE).isValid shouldBe true
|
||||
num(DataType.BYTE, 42.0).convertTypeKeepValue(DataType.UBYTE).isValid shouldBe true
|
||||
num(DataType.UWORD, 12345.0).convertTypeKeepValue(DataType.WORD).isValid shouldBe true
|
||||
num(DataType.WORD, 12345.0).convertTypeKeepValue(DataType.UWORD).isValid shouldBe true
|
||||
}
|
||||
})
|
||||
|
@ -857,4 +857,34 @@ main {
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[1] shouldContain "undefined symbol"
|
||||
}
|
||||
|
||||
test("return unsigned values for signed results ok if value fits") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
void foo()
|
||||
void bar()
|
||||
void overflow1()
|
||||
void overflow2()
|
||||
}
|
||||
|
||||
sub foo() -> byte {
|
||||
return 42
|
||||
}
|
||||
sub bar() -> word {
|
||||
return 12345
|
||||
}
|
||||
sub overflow1() -> byte {
|
||||
return 200
|
||||
}
|
||||
sub overflow2() -> word {
|
||||
return 44444
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "17:16: type UBYTE of return value doesn't match subroutine's return type BYTE"
|
||||
errors.errors[1] shouldContain "20:16: type UWORD of return value doesn't match subroutine's return type WORD"
|
||||
}
|
||||
})
|
||||
|
@ -612,6 +612,9 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
||||
}
|
||||
|
||||
private fun internalCast(targettype: DataType, implicit: Boolean): ValueAfterCast {
|
||||
|
||||
// NOTE: this MAY convert a value into another when switching from singed to unsigned!!!
|
||||
|
||||
if(type==targettype)
|
||||
return ValueAfterCast(true, null, this)
|
||||
if (implicit) {
|
||||
@ -736,6 +739,49 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
||||
}
|
||||
return ValueAfterCast(false, "no cast available from $type to $targettype", null)
|
||||
}
|
||||
|
||||
fun convertTypeKeepValue(targetDt: DataType): ValueAfterCast {
|
||||
if(type==targetDt)
|
||||
return ValueAfterCast(true, null, this)
|
||||
|
||||
when(type) {
|
||||
DataType.UBYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.BYTE -> if(number<=127.0) return cast(targetDt, false)
|
||||
DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT -> return cast(targetDt, false)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.UWORD -> if(number>=0.0) return cast(targetDt, false)
|
||||
DataType.WORD, DataType.LONG, DataType.FLOAT -> return cast(targetDt, false)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE -> if(number<=255.0) return cast(targetDt, false)
|
||||
DataType.BYTE -> if(number<=127.0) return cast(targetDt, false)
|
||||
DataType.WORD -> if(number<=32767.0) return cast(targetDt, false)
|
||||
DataType.LONG, DataType.FLOAT -> return cast(targetDt, false)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE -> if(number in 0.0..255.0) return cast(targetDt, false)
|
||||
DataType.BYTE -> if(number in -128.0..127.0) return cast(targetDt, false)
|
||||
DataType.UWORD -> if(number in 0.0..32767.0) return cast(targetDt, false)
|
||||
DataType.LONG, DataType.FLOAT -> return cast(targetDt, false)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
DataType.LONG, DataType.FLOAT -> return cast(targetDt, false)
|
||||
else -> {}
|
||||
}
|
||||
return ValueAfterCast(false, "no type conversion possible from $type to $targetDt", null)
|
||||
}
|
||||
}
|
||||
|
||||
class CharLiteral private constructor(val value: Char,
|
||||
|
@ -1,10 +1,6 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
Don't allow assigning str to array!
|
||||
Don't allow assigning array to str!
|
||||
Don't allow assigning a word to an array or string!
|
||||
|
||||
Put palette fade to white / black in.
|
||||
|
||||
Regenerate skeleton doc files.
|
||||
|
Loading…
Reference in New Issue
Block a user