added several compiler checks against weird boolean type use in expressions

This commit is contained in:
Irmen de Jong 2022-07-07 02:52:13 +02:00
parent 9500fc11ac
commit bdb7de34be
7 changed files with 77 additions and 39 deletions

View File

@ -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) {

View File

@ -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)
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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")

View File

@ -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.

View File

@ -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
}
}