| // TARGET_BACKEND: NATIVE | |
| // FILECHECK_STAGE: CStubs | |
| // FREE_COMPILER_ARGS: -Xbinary=genericSafeCasts=true -Xdisable-phases=ComputeTypesPass | |
| // IGNORE_NATIVE: optimizationMode=OPT && cacheMode=STATIC_ONLY_DIST | |
| // IGNORE_NATIVE: optimizationMode=OPT && cacheMode=STATIC_EVERYWHERE | |
| // KT-86948: CastsOptimization must not merge a type check/cast result across loop | |
| // iterations as if a node reached on one iteration is reached on every one. | |
| // ComputeTypesPass is disabled so that CastsOptimization is tested in isolation | |
| // (ComputeTypesPass mishandles the same case, see KT-86949). | |
| class A | |
| fun bar() = true | |
| fun consume(b: Boolean) = b | |
| // In all three tests below there is a similar bug: on the first loop iteration | |
| // the argument resolves to a known variable (an `A`), but on the second one | |
| // it does not, so the check is not statically known and must be kept. | |
| // The type check is in a general expression position (the `visitTypeOperator` path). | |
| // CHECK-LABEL: define {{i1|zeroext i1}} @"kfun:#testa(){}kotlin.Boolean" | |
| fun testa(): Boolean { | |
| val a = A() | |
| var x: Any = a | |
| var count = 0 | |
| var last = false | |
| do { | |
| count++ | |
| // CHECK-DEBUG: {{call|call zeroext}} i1 @IsSubtype | |
| // CHECK-OPT: {{call|call zeroext}} i1 @IsSubclassFast | |
| last = consume((if (bar()) x else a) is A) | |
| x = Any() | |
| } while (count < 2) | |
| return last | |
| // CHECK-LABEL: epilogue: | |
| } | |
| // The type check is assigned to a Boolean variable (the `buildBooleanPredicate` path). | |
| // CHECK-LABEL: define {{i1|zeroext i1}} @"kfun:#testb(){}kotlin.Boolean" | |
| fun testb(): Boolean { | |
| val a = A() | |
| var x: Any = a | |
| var count = 0 | |
| var last = false | |
| do { | |
| count++ | |
| // CHECK-DEBUG: {{call|call zeroext}} i1 @IsSubtype | |
| // CHECK-OPT: {{call|call zeroext}} i1 @IsSubclassFast | |
| last = (if (bar()) x else a) is A | |
| x = Any() | |
| } while (count < 2) | |
| return last | |
| // CHECK-LABEL: epilogue: | |
| } | |
| // The safe cast is assigned to a nullable variable (the `buildNullablePredicate` path). | |
| // CHECK-LABEL: define i32 @"kfun:#testc(){}kotlin.Int" | |
| fun testc(): Int { | |
| val a = A() | |
| var x: Any = a | |
| var count = 0 | |
| var last: A? = a | |
| do { | |
| count++ | |
| // CHECK-DEBUG: {{call|call zeroext}} i1 @IsSubtype | |
| // CHECK-OPT: {{call|call zeroext}} i1 @IsSubclassFast | |
| last = (if (bar()) x else a) as? A | |
| x = Any() | |
| } while (count < 2) | |
| return if (last == null) 0 else 1 | |
| // CHECK-LABEL: epilogue: | |
| } | |
| // CHECK-LABEL: define ptr @"kfun:#box(){}kotlin.String" | |
| fun box(): String { | |
| if (testa()) return "FAIL KT-86948 (general position): type check wrongly optimized" | |
| if (testb()) return "FAIL KT-86948 (boolean var): type check wrongly optimized" | |
| if (testc() != 0) return "FAIL KT-86948 (safe cast): cast wrongly optimized" | |
| return "OK" | |
| } |