mirror of
https://github.com/irmen/prog8.git
synced 2025-02-16 07:31:48 +00:00
added several compiler checks against weird boolean type use in expressions
This commit is contained in:
parent
9500fc11ac
commit
bdb7de34be
@ -193,6 +193,16 @@ class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||
}
|
||||
}
|
||||
|
||||
// boolvar & 1 --> boolvar
|
||||
// boolvar & 2 --> false
|
||||
if(expr.operator=="&" && rightDt in IntegerDatatypes && (leftDt == DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||
if(rightVal?.number==1.0) {
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
} else if(rightVal?.number!=null && (rightVal.number.toInt() and 1)==0) {
|
||||
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
|
||||
}
|
||||
}
|
||||
|
||||
// simplify when a term is constant and directly determines the outcome
|
||||
val constFalse = NumericLiteral.fromBoolean(false, expr.position)
|
||||
val newExpr: Expression? = when (expr.operator) {
|
||||
|
@ -462,6 +462,11 @@ internal class AstChecker(private val program: Program,
|
||||
if(numvalue!=null && targetDt.isKnown)
|
||||
checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue)
|
||||
|
||||
if(assignment.isAugmentable && targetDt istype DataType.BOOL) {
|
||||
val operator = (assignment.value as? BinaryExpression)?.operator
|
||||
if(operator in setOf("-", "*", "/", "%"))
|
||||
errors.err("can't use boolean operand with this operator $operator", assignment.position)
|
||||
}
|
||||
super.visit(assignment)
|
||||
}
|
||||
|
||||
@ -890,6 +895,31 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(leftDt==DataType.BOOL || rightDt==DataType.BOOL ||
|
||||
(expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true ||
|
||||
(expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true) {
|
||||
if(expr.operator in setOf("<", "<=", ">", ">=")) {
|
||||
errors.err("can't use boolean operand with this comparison operator", expr.position)
|
||||
}
|
||||
if(expr.operator in setOf("-", "*", "/", "%") && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||
errors.err("can't use boolean operand with this operator", expr.left.position)
|
||||
}
|
||||
if(expr.operator=="+" && (leftDt==DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||
val rightNum = expr.right.constValue(program)?.number ?: 0.0
|
||||
if(rightNum > 1.0)
|
||||
errors.err("can't use boolean operand with this operator", expr.left.position)
|
||||
}
|
||||
if(expr.operator == "==" || expr.operator == "!=") {
|
||||
val leftNum = expr.left.constValue(program)?.number ?: 0.0
|
||||
val rightNum = expr.right.constValue(program)?.number ?: 0.0
|
||||
if(leftNum>1.0 || rightNum>1.0 || leftNum<0.0 || rightNum<0.0) {
|
||||
errors.warn("expression is always false", expr.position)
|
||||
}
|
||||
}
|
||||
if((expr.operator == "/" || expr.operator == "%") && ( rightDt==DataType.BOOL || (expr.right as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) {
|
||||
errors.err("can't use boolean operand with this operator", expr.right.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(typecast: TypecastExpression) {
|
||||
@ -1121,6 +1151,11 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
// else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/--
|
||||
|
||||
if(postIncrDecr.target.inferType(program) istype DataType.BOOL) {
|
||||
errors.err("can't use boolean operand with this operator", postIncrDecr.position)
|
||||
}
|
||||
|
||||
super.visit(postIncrDecr)
|
||||
}
|
||||
|
||||
|
@ -137,6 +137,22 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
// replace BOOL return type and parameters by UBYTE
|
||||
if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) {
|
||||
val newReturnTypes = subroutine.returntypes.map {
|
||||
if(it==DataType.BOOL) DataType.UBYTE else it
|
||||
}
|
||||
val newParams = subroutine.parameters.map {
|
||||
if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it
|
||||
}.toMutableList()
|
||||
val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes,
|
||||
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers,
|
||||
subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements,
|
||||
subroutine.position)
|
||||
return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent))
|
||||
}
|
||||
|
||||
|
||||
// Most code generation targets only support subroutine inlining on asmsub subroutines
|
||||
// So we reset the flag here to be sure it doesn't cause problems down the line in the codegen.
|
||||
if(!subroutine.isAsmSubroutine && options.compTarget.name!=VMTarget.NAME)
|
||||
|
@ -222,23 +222,5 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(subroutine.returntypes.any { it==DataType.BOOL } || subroutine.parameters.any {it.type==DataType.BOOL}) {
|
||||
val newReturnTypes = subroutine.returntypes.map {
|
||||
if(it==DataType.BOOL) DataType.UBYTE else it
|
||||
}
|
||||
val newParams = subroutine.parameters.map {
|
||||
if(it.type==DataType.BOOL) SubroutineParameter(it.name, DataType.UBYTE, it.position) else it
|
||||
}.toMutableList()
|
||||
val newSubroutine = Subroutine(subroutine.name, newParams, newReturnTypes,
|
||||
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers,
|
||||
subroutine.asmAddress, subroutine.isAsmSubroutine, subroutine.inline, subroutine.statements,
|
||||
subroutine.position)
|
||||
return listOf(IAstModification.ReplaceNode(subroutine, newSubroutine, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ import prog8.code.core.ZeropageWish
|
||||
fun Program.getTempVar(dt: DataType, altNames: Boolean=false): Pair<List<String>, VarDecl> {
|
||||
val tmpvarName = if(altNames) {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> listOf("prog8_lib", "tempvar_ub2")
|
||||
DataType.UBYTE, DataType.BOOL -> listOf("prog8_lib", "tempvar_ub2")
|
||||
DataType.BYTE -> listOf("prog8_lib", "tempvar_b2")
|
||||
DataType.UWORD -> listOf("prog8_lib", "tempvar_uw2")
|
||||
DataType.WORD -> listOf("prog8_lib", "tempvar_w2")
|
||||
@ -23,7 +23,7 @@ fun Program.getTempVar(dt: DataType, altNames: Boolean=false): Pair<List<String>
|
||||
}
|
||||
} else {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> listOf("prog8_lib", "tempvar_ub")
|
||||
DataType.UBYTE, DataType.BOOL -> listOf("prog8_lib", "tempvar_ub")
|
||||
DataType.BYTE -> listOf("prog8_lib", "tempvar_b")
|
||||
DataType.UWORD -> listOf("prog8_lib", "tempvar_uw")
|
||||
DataType.WORD -> listOf("prog8_lib", "tempvar_w")
|
||||
|
@ -3,9 +3,8 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- add compiler error when using boolean with operators <,<=,>,>= (== and != are ok!)
|
||||
- add compiler error when using boolean on lhs of operators -,*,/,% (+ is ok!)
|
||||
- add compiler error when using boolean on rhs operators /,%
|
||||
- add ARRAY_OF_BOOL array type
|
||||
|
||||
- test various scenarios of using bool type vs byte actually produces tighter code
|
||||
make unit test of them?
|
||||
|
||||
@ -24,7 +23,6 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- bool data type? see below
|
||||
- add some more optimizations in vmPeepholeOptimizer
|
||||
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
|
||||
this info is needed for more advanced optimizations and later code generation steps.
|
||||
|
@ -5,23 +5,20 @@ main {
|
||||
|
||||
bool boolvalue1 = true
|
||||
bool boolvalue2 = false
|
||||
ubyte ubvalue1 = true
|
||||
ubyte ubvalue2 = false
|
||||
ubyte @shared ubvalue1 = true
|
||||
ubyte @shared ubvalue2 = false
|
||||
|
||||
sub thing(bool b1, bool b2) -> bool {
|
||||
return b1 and b2
|
||||
}
|
||||
|
||||
sub start() {
|
||||
if ubvalue1<44 or ubvalue1>99
|
||||
txt.print("0\n")
|
||||
|
||||
if boolvalue1 or boolvalue2
|
||||
txt.print("1\n")
|
||||
|
||||
if boolvalue1 and boolvalue2
|
||||
txt.print("2\n")
|
||||
|
||||
; if ubvalue1 or ubvalu2
|
||||
; txt.print("3\n")
|
||||
;
|
||||
; if ubvalue1 and ubvalu2
|
||||
; txt.print("4\n")
|
||||
ubvalue1 = boolvalue1 & 1
|
||||
ubvalue2 = 1 & boolvalue1
|
||||
ubvalue1 = boolvalue1 & 0
|
||||
ubvalue2 = boolvalue1 & 8
|
||||
boolvalue1 = boolvalue2 & 1 ==0
|
||||
; boolvar & 1 -> boolvar, (boolvar & 1 == 0) -> not boolvar
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user