| // TARGET_BACKEND: NATIVE | |
| // FILECHECK_STAGE: CStubs | |
| // FREE_COMPILER_ARGS: -Xbinary=genericSafeCasts=true | |
| // IGNORE_NATIVE: optimizationMode=OPT && cacheMode=STATIC_ONLY_DIST | |
| // IGNORE_NATIVE: optimizationMode=OPT && cacheMode=STATIC_EVERYWHERE | |
| open class RTObject | |
| class Glue : RTObject() | |
| class StringValue : RTObject() | |
| class Other | |
| // CHECK-LABEL: define {{i1|zeroext i1}} @"kfun:#reproduce(RTObject;kotlin.Any){}kotlin.Boolean" | |
| fun reproduce(obj: RTObject, o: Any): Boolean { | |
| val glue = if (obj is Glue) obj else null | |
| val text = if (obj is StringValue) obj else null | |
| if (glue == null && text != null) { | |
| // This block is reachable, so `o is Other` must be kept, not folded to a constant. | |
| // CHECK-DEBUG: @IsSubtype{{.*}}@"kclass:Other" | |
| // CHECK-OPT: {{call|call zeroext}} i1 @IsSubclassFast | |
| return o is Other | |
| } | |
| return false | |
| // CHECK-LABEL: epilogue: | |
| } | |
| // CHECK-LABEL: define {{i1|zeroext i1}} @"kfun:#reproduce2(Glue?;kotlin.Any){}kotlin.Boolean" | |
| fun reproduce2(v: Glue?, o: Any): Boolean { | |
| val x = if (v is Glue?) v else null | |
| if (x == null) { | |
| // Reachable when v == null, so `o is Other` must be kept, not folded to a constant. | |
| // CHECK-DEBUG: @IsSubtype{{.*}}@"kclass:Other" | |
| // CHECK-OPT: {{call|call zeroext}} i1 @IsSubclassFast | |
| return o is Other | |
| } | |
| return false | |
| // CHECK-LABEL: epilogue: | |
| } | |
| // CHECK-LABEL: define {{i1|zeroext i1}} @"kfun:#reproduce3(RTObject;kotlin.Any){}kotlin.Boolean" | |
| fun reproduce3(obj: RTObject, o: Any): Boolean { | |
| val glue = if (obj as? Glue != null) obj else null | |
| val text = if (obj as? StringValue != null) obj else null | |
| if (glue == null && text != null) { | |
| // This block is reachable, so `o is Other` must be kept, not folded to a constant. | |
| // CHECK-DEBUG: @IsSubtype{{.*}}@"kclass:Other" | |
| // CHECK-OPT: {{call|call zeroext}} i1 @IsSubclassFast | |
| return o is Other | |
| } | |
| return false | |
| // CHECK-LABEL: epilogue: | |
| } | |
| // CHECK-LABEL: define {{i1|zeroext i1}} @"kfun:#reproduce4(kotlin.Any;kotlin.Any?){}kotlin.Boolean" | |
| fun reproduce4(obj: Any, t: Any?): Boolean { | |
| // CHECK-DEBUG: @IsSubtype{{.*}}@"kclass:Other" | |
| if (obj is Other) { | |
| // Reachable with t == null, when the safe call evaluates to null, so the `is Other` check on | |
| // its result must be kept, not folded to a constant despite the safe call's result aliasing | |
| // to `obj`. The explicit type arguments keep the frontend from narrowing the safe call's type | |
| // (which would legitimately reduce the check to a null check). | |
| // CHECK-DEBUG: @IsSubtype{{.*}}@"kclass:Other" | |
| // CHECK-OPT: {{call|call zeroext}} i1 @IsSubclassFast | |
| return (t?.let<Any, Any> { obj }) is Other | |
| } | |
| return false | |
| // CHECK-LABEL: epilogue: | |
| } | |
| // CHECK-LABEL: define ptr @"kfun:#box(){}kotlin.String" | |
| fun box(): String { | |
| if (reproduce(StringValue(), Any())) return "FAIL KT-87261 (type check): o is Other folded to true" | |
| if (reproduce2(null, Any())) return "FAIL KT-87261 (nullable type operand): o is Other folded to true" | |
| if (reproduce3(StringValue(), Any())) return "FAIL KT-87261 (safe type check): o is Other folded to true" | |
| if (reproduce4(Other(), null)) return "FAIL KT-87261 (safe call with unrelated result): is Other folded to true" | |
| return "OK" | |
| } |