Skip to content

Commit 3dda59f

Browse files
committed
Sema: improve function cast checking
Functions can in fact conform to some protocols, notably, marker protocols. Dynamically they have no influence on whether the cast will succeed or not, as there is no way to tell if a type conforms to such a protocol at runtime. But we should check them statically because they otherwise can be cast to Any eventually.
1 parent 3ab4c75 commit 3dda59f

File tree

3 files changed

+53
-26
lines changed

3 files changed

+53
-26
lines changed

lib/Sema/TypeCheckConstraints.cpp

+26-22
Original file line numberDiff line numberDiff line change
@@ -1957,29 +1957,33 @@ TypeChecker::typeCheckCheckedCast(Type fromType, Type toType,
19571957

19581958
// A cast from a function type to an existential type (except `Any`)
19591959
// or an archetype type (with constraints) cannot succeed
1960-
auto toArchetypeType = toType->is<ArchetypeType>();
1961-
auto fromFunctionType = fromType->is<FunctionType>();
1962-
auto toExistentialType = toType->isAnyExistentialType();
1963-
1964-
auto toConstrainedArchetype = false;
1965-
if (toArchetypeType) {
1966-
auto archetype = toType->castTo<ArchetypeType>();
1967-
toConstrainedArchetype = !archetype->getConformsTo().empty();
1968-
}
1969-
1970-
if (fromFunctionType &&
1971-
(toExistentialType || (toArchetypeType && toConstrainedArchetype))) {
1972-
switch (contextKind) {
1973-
case CheckedCastContextKind::None:
1974-
case CheckedCastContextKind::ConditionalCast:
1975-
case CheckedCastContextKind::ForcedCast:
1976-
return CheckedCastKind::Unresolved;
1960+
if (fromType->is<FunctionType>()) {
1961+
auto toArchetypeType = toType->is<ArchetypeType>();
1962+
auto toExistentialType = toType->isAnyExistentialType();
1963+
1964+
auto conformsToAllProtocols = true;
1965+
if (toArchetypeType) {
1966+
auto archetype = toType->castTo<ArchetypeType>();
1967+
conformsToAllProtocols = llvm::all_of(archetype->getConformsTo(),
1968+
[&](ProtocolDecl *proto) {
1969+
return module->checkConformance(fromType, proto,
1970+
/*allowMissing=*/false);
1971+
});
1972+
}
19771973

1978-
case CheckedCastContextKind::IsPattern:
1979-
case CheckedCastContextKind::EnumElementPattern:
1980-
case CheckedCastContextKind::IsExpr:
1981-
case CheckedCastContextKind::Coercion:
1982-
break;
1974+
if (toExistentialType || (toArchetypeType && !conformsToAllProtocols)) {
1975+
switch (contextKind) {
1976+
case CheckedCastContextKind::None:
1977+
case CheckedCastContextKind::ConditionalCast:
1978+
case CheckedCastContextKind::ForcedCast:
1979+
return CheckedCastKind::Unresolved;
1980+
1981+
case CheckedCastContextKind::IsPattern:
1982+
case CheckedCastContextKind::EnumElementPattern:
1983+
case CheckedCastContextKind::IsExpr:
1984+
case CheckedCastContextKind::Coercion:
1985+
break;
1986+
}
19831987
}
19841988
}
19851989

test/Interpreter/casts.swift

+10
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,13 @@ Casts.test("testCastProtocolWithAnyObjectToProtocolCompoTypeSuperclass") {
142142
let shouldBeNil = (F() as QAny) as? KQAny
143143
expectNil(shouldBeNil)
144144
}
145+
146+
func f1() -> Any { return 1 }
147+
func attemptFunctionCast<U>(_ u: U.Type) -> U? {
148+
return f1 as? U
149+
}
150+
Casts.test("testFunctionCastToArchetype") {
151+
expectNil(attemptFunctionCast(Int.self))
152+
expectNil(attemptFunctionCast(K.self))
153+
expectTrue(attemptFunctionCast(Any.self) != nil)
154+
}

test/expr/cast/as_coerce.swift

+17-4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ protocol P_48579 {}
119119
do {
120120
func f1() -> Any {}
121121
func f2() {}
122+
@Sendable func f3() {}
122123

123124
_ = f1 is P_48579 // expected-warning {{cast from '() -> Any' to unrelated type 'any P_48579' always fails}} // expected-note {{did you mean to call 'f1' with '()'?}}{{9-9=()}}
124125
_ = f1 as! P_48579 // expected-warning {{cast from '() -> Any' to unrelated type 'any P_48579' always fails}} // expected-note {{did you mean to call 'f1' with '()'?}}{{9-9=()}}
@@ -133,20 +134,32 @@ do {
133134
_ = f2 as? Any // expected-warning {{conditional cast from '() -> ()' to 'Any' always succeeds}}
134135

135136

136-
func test1<T: P_48579>(_: T.Type) {
137+
func test1<T: P_48579, V: P_48579 & Sendable>(_: T.Type, _: V.Type) {
137138
_ = f1 is T // expected-warning {{cast from '() -> Any' to unrelated type 'T' always fails}} // expected-note {{did you mean to call 'f1' with '()'?}}{{11-11=()}}
138-
_ = f1 as! T // expected-warning {{cast from '() -> Any' to unrelated type 'T' always fails}} // expected-note {{did you mean to call 'f1' with '()'?}}{{11-11=()}}
139+
_ = f1 as! V // expected-warning {{cast from '() -> Any' to unrelated type 'V' always fails}} // expected-note {{did you mean to call 'f1' with '()'?}}{{11-11=()}}
139140
_ = f1 as? T // expected-warning {{cast from '() -> Any' to unrelated type 'T' always fails}} // expected-note {{did you mean to call 'f1' with '()'?}}{{11-11=()}}
140141
_ = f2 is T // expected-warning {{cast from '() -> ()' to unrelated type 'T' always fails}}
141-
_ = f2 as! T // expected-warning {{cast from '() -> ()' to unrelated type 'T' always fails}}
142+
_ = f2 as! V // expected-warning {{cast from '() -> ()' to unrelated type 'V' always fails}}
142143
_ = f2 as? T // expected-warning {{cast from '() -> ()' to unrelated type 'T' always fails}}
144+
_ = f3 is T // expected-warning {{cast from '@Sendable () -> ()' to unrelated type 'T' always fails}}
145+
_ = f3 as! V // expected-warning {{cast from '@Sendable () -> ()' to unrelated type 'V' always fails}}
146+
_ = f3 as? T // expected-warning {{cast from '@Sendable () -> ()' to unrelated type 'T' always fails}}
143147
}
144148

145-
func test2<U>(_: U.Type) {
149+
func test2<U, S: Sendable>(_: U.Type, _: S.Type) {
150+
_ = f1 is U // Okay
146151
_ = f1 as! U // Okay
147152
_ = f1 as? U // Okay
153+
_ = f1 is U // Okay
148154
_ = f2 as! U // Okay
149155
_ = f2 as? U // Okay
156+
157+
_ = f2 is S // expected-warning {{cast from '() -> ()' to unrelated type 'S' always fails}}
158+
_ = f2 as! S // expected-warning {{cast from '() -> ()' to unrelated type 'S' always fails}}
159+
_ = f2 as? S // expected-warning {{cast from '() -> ()' to unrelated type 'S' always fails}}
160+
_ = f3 is S // Okay
161+
_ = f3 as! S // Okay
162+
_ = f3 as? S // Okay
150163
}
151164
}
152165

0 commit comments

Comments
 (0)