已合并
instanceof+interface unexpected narrows to never #11175
ajq257257创建于 14 天前
instanceof+interface unexpected narrows to never #11175
已合并
共 8 个文件变更+223-5
| @@ -1176,7 +1176,7 @@ static void ReportIfInstanceofTrivialKnown(ETSChecker *checker, Type *lhs, Type | |||
| 1176 | checker->LogDiagnostic(diagnostic::INSTANCEOF_ALWAYS_KNOWN, | 1176 | checker->LogDiagnostic(diagnostic::INSTANCEOF_ALWAYS_KNOWN, |
| 1177 | util::DiagnosticMessageParams {INSTANCEOF_ALWAYS_TRUE_WORD}, pos); | 1177 | util::DiagnosticMessageParams {INSTANCEOF_ALWAYS_TRUE_WORD}, pos); |
| 1178 | } | 1178 | } |
| 1179 | - } else if (!HasInterfaceConstituent(lhs) || !HasInterfaceConstituent(rhs)) { | 1179 | + } else if (!HasInterfaceConstituent(lhs) && !HasInterfaceConstituent(rhs)) { |
| 1180 | checker->LogDiagnostic(diagnostic::INSTANCEOF_ALWAYS_KNOWN, | 1180 | checker->LogDiagnostic(diagnostic::INSTANCEOF_ALWAYS_KNOWN, |
| 1181 | util::DiagnosticMessageParams {INSTANCEOF_ALWAYS_FALSE_WORD}, pos); | 1181 | util::DiagnosticMessageParams {INSTANCEOF_ALWAYS_FALSE_WORD}, pos); |
| 1182 | } | 1182 | } |
| @@ -1508,6 +1508,12 @@ std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSObjectType *te | |||
| 1508 | return {testedType, GetGlobalTypesHolder()->GlobalETSNeverType()}; | 1508 | return {testedType, GetGlobalTypesHolder()->GlobalETSNeverType()}; |
| 1509 | } | 1509 | } |
| 1510 | 1510 | ||
| 1511 | + // When neither type is a supertype of the other, instanceof can still succeed if | ||
| 1512 | + // at least one type is an interface. | ||
| 1513 | + if (testedType->IsInterface() || objectType->IsInterface()) { | ||
| 1514 | + return {testedType, actualType}; | ||
| 1515 | + } | ||
| 1516 | + | ||
| 1511 | return {GetGlobalTypesHolder()->GlobalETSNeverType(), actualType}; | 1517 | return {GetGlobalTypesHolder()->GlobalETSNeverType(), actualType}; |
| 1512 | } | 1518 | } |
| 1513 | 1519 | ||
| @@ -0,0 +1,83 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +// Test that instanceof between an interface-typed variable and an unrelated | ||
| 17 | +// class does NOT produce "always false" warning (W1001506), because a class | ||
| 18 | +// implementing the interface may also extend the tested class. | ||
| 19 | + | ||
| 20 | +interface I { | ||
| 21 | + foo(): void | ||
| 22 | +} | ||
| 23 | + | ||
| 24 | +class A { | ||
| 25 | + bar(): void {} | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +class C extends A implements I { | ||
| 29 | + foo(): void {} | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +class D {} | ||
| 33 | + | ||
| 34 | +// interface variable instanceof unrelated class — no warning | ||
| 35 | +function ifaceToClass(i: I): boolean { | ||
| 36 | + return i instanceof A | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +// interface variable instanceof unrelated interface — no warning | ||
| 40 | +function ifaceToIface(i: I): boolean { | ||
| 41 | + return i instanceof D | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +// class variable instanceof unrelated interface — no warning | ||
| 45 | +function classToIface(a: A): boolean { | ||
| 46 | + return a instanceof I | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +// class variable instanceof unrelated class — SHOULD warn "always false" | ||
| 50 | +function classToClass(a: A): boolean { | ||
| 51 | + return a instanceof D | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +// Smart narrowing: after if (i instanceof A), i is narrowed to A, | ||
| 55 | +// so i instanceof A is "always true" | ||
| 56 | +function smartNarrowTrue(i: I): boolean { | ||
| 57 | + if (i instanceof A) { | ||
| 58 | + return i instanceof A | ||
| 59 | + } | ||
| 60 | + return false | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +// Smart narrowing: after if (i instanceof A), i is narrowed to A, | ||
| 64 | +// then i instanceof D (unrelated class to A) is "always false" | ||
| 65 | +function smartNarrowFalse(i: I): boolean { | ||
| 66 | + if (i instanceof A) { | ||
| 67 | + return i instanceof D | ||
| 68 | + } | ||
| 69 | + return false | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +// Smart narrowing: after if (i instanceof A), i is narrowed to A, | ||
| 73 | +// then i instanceof I is NOT "always true" | ||
| 74 | +function smartNarrowBackToIface(i: I): boolean { | ||
| 75 | + if (i instanceof A) { | ||
| 76 | + return i instanceof I | ||
| 77 | + } | ||
| 78 | + return false | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +/* @@? 51:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ | ||
| 82 | +/* @@? 58:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | ||
| 83 | +/* @@? 67:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ | ||
| @@ -26,6 +26,5 @@ function qux(x: A): boolean { | |||
| 26 | return false | 26 | return false |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | -/* @@? 21:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | ||
| 30 | /* @@? 22:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | 29 | /* @@? 22:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ |
| 31 | /* @@? 23:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | 30 | /* @@? 23:17 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ |
| @@ -48,5 +48,4 @@ function quz(x: C): boolean { | |||
| 48 | 48 | ||
| 49 | /* @@? 30:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | 49 | /* @@? 30:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ |
| 50 | /* @@? 34:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ | 50 | /* @@? 34:12 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ |
| 51 | -/* @@? 43:21 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | ||
| 52 | /* @@? 44:21 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | 51 | /* @@? 44:21 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ |
| @@ -61,7 +61,6 @@ function quux(x: string | undefined): number { | |||
| 61 | return x.length + (t1 ? 1 : 0) + (t2 ? 1 : 0) | 61 | return x.length + (t1 ? 1 : 0) + (t2 ? 1 : 0) |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | -/* @@? 31:9 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ | ||
| 65 | /* @@? 32:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | 64 | /* @@? 32:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ |
| 66 | /* @@? 39:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ | 65 | /* @@? 39:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as true */ |
| 67 | /* @@? 46:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ | 66 | /* @@? 46:16 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ |
| @@ -62,7 +62,6 @@ class C<T> { | |||
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | 64 | ||
| 65 | -/* @@? 20:8 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ | ||
| 66 | /* @@? 20:28 Error Syntax error ESY0130: Invalid right-hand side in 'instanceof' expression. */ | 65 | /* @@? 20:28 Error Syntax error ESY0130: Invalid right-hand side in 'instanceof' expression. */ |
| 67 | /* @@? 36:9 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ | 66 | /* @@? 36:9 Warning Warning W1001506: the value of the instanceof expression is known at compile-time as false */ |
| 68 | /* @@? 36:22 Error Syntax error ESY0130: Invalid right-hand side in 'instanceof' expression. */ | 67 | /* @@? 36:22 Error Syntax error ESY0130: Invalid right-hand side in 'instanceof' expression. */ |
| @@ -0,0 +1,133 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +interface IBase { | ||
| 17 | + getVal(): int | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +interface IUnrelated {} | ||
| 21 | + | ||
| 22 | +class Base { | ||
| 23 | + field: int = 0 | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +class Derived extends Base implements IBase { | ||
| 27 | + constructor(v: int) { | ||
| 28 | + super() | ||
| 29 | + this.field = v | ||
| 30 | + } | ||
| 31 | + getVal(): int { | ||
| 32 | + return this.field | ||
| 33 | + } | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +class Other {} | ||
| 37 | + | ||
| 38 | +// Case 1: interface variable holding a Derived instance, instanceof a Base class | ||
| 39 | +function testInterfaceToClass(): int { | ||
| 40 | + let i: IBase = new Derived(42) | ||
| 41 | + let b: Base | ||
| 42 | + try { | ||
| 43 | + if (i instanceof Base) { | ||
| 44 | + b = i as Base | ||
| 45 | + return b.field | ||
| 46 | + } | ||
| 47 | + } catch (e) { | ||
| 48 | + return -1 | ||
| 49 | + } | ||
| 50 | + return -2 | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +// Case 2: interface variable, instanceof an unrelated interface | ||
| 54 | +function testInterfaceToInterface(): int { | ||
| 55 | + let i: IBase = new Derived(10) | ||
| 56 | + try { | ||
| 57 | + if (i instanceof IUnrelated) { | ||
| 58 | + return -1 | ||
| 59 | + } | ||
| 60 | + return i.getVal() | ||
| 61 | + } catch (e) { | ||
| 62 | + return -2 | ||
| 63 | + } | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +// Case 3: class variable, instanceof an interface | ||
| 67 | +function testClassToInterface(): int { | ||
| 68 | + let b: Base = new Derived(99) | ||
| 69 | + try { | ||
| 70 | + if (b instanceof IBase) { | ||
| 71 | + let i: IBase = b as IBase | ||
| 72 | + return i.getVal() | ||
| 73 | + } | ||
| 74 | + } catch (e) { | ||
| 75 | + return -1 | ||
| 76 | + } | ||
| 77 | + return -2 | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +// Case 4: interface variable, instanceof truly unrelated class | ||
| 81 | +function testInterfaceToUnrelatedClass(): int { | ||
| 82 | + let i: IBase = new Derived(5) | ||
| 83 | + if (i instanceof Other) { | ||
| 84 | + return -1 | ||
| 85 | + } | ||
| 86 | + return i.getVal() | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +// Case 5: Diamond-like — two interfaces, one class implementing both | ||
| 90 | +interface IA { | ||
| 91 | + a(): int | ||
| 92 | +} | ||
| 93 | +interface IB { | ||
| 94 | + b(): int | ||
| 95 | +} | ||
| 96 | +class Diamond implements IA, IB { | ||
| 97 | + a(): int { return 1 } | ||
| 98 | + b(): int { return 2 } | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +function testDiamondInterfaceNarrowing(): int { | ||
| 102 | + let x: IA = new Diamond() | ||
| 103 | + try { | ||
| 104 | + if (x instanceof IB) { | ||
| 105 | + let y: IB = x as IB | ||
| 106 | + return y.b() | ||
| 107 | + } | ||
| 108 | + } catch (e) { | ||
| 109 | + return -1 | ||
| 110 | + } | ||
| 111 | + return -2 | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +// Case 6: instanceof in else-if chain with interface narrowing | ||
| 115 | +function testElseIfChain(): int { | ||
| 116 | + let x: Object = new Derived(77) | ||
| 117 | + if (x instanceof IBase) { | ||
| 118 | + let i: IBase = x as IBase | ||
| 119 | + return i.getVal() | ||
| 120 | + } else if (x instanceof Other) { | ||
| 121 | + return -1 | ||
| 122 | + } | ||
| 123 | + return -2 | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +function main(): void { | ||
| 127 | + arktest.assertEQ(testInterfaceToClass(), 42) | ||
| 128 | + arktest.assertEQ(testInterfaceToInterface(), 10) | ||
| 129 | + arktest.assertEQ(testClassToInterface(), 99) | ||
| 130 | + arktest.assertEQ(testInterfaceToUnrelatedClass(), 5) | ||
| 131 | + arktest.assertEQ(testDiamondInterfaceNarrowing(), 2) | ||
| 132 | + arktest.assertEQ(testElseIfChain(), 77) | ||
| 133 | +} | ||