Skip to content

Commit 2e2c12f

Browse files
authored
Merge pull request #22822 from theblixguy/fix/SR-6022
[Typechecker] Warn when casting a function type to an existential or archetype type
2 parents 7081293 + a493934 commit 2e2c12f

File tree

3 files changed

+78
-0
lines changed

3 files changed

+78
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,8 @@ WARNING(is_expr_same_type,none,
901901
"'!= nil'?", (Type, Type))
902902
WARNING(downcast_to_unrelated,none,
903903
"cast from %0 to unrelated type %1 always fails", (Type, Type))
904+
NOTE(downcast_to_unrelated_fixit,none,
905+
"did you mean to call %0 with '()'?", (Identifier))
904906
ERROR(downcast_to_more_optional,none,
905907
"cannot downcast from %0 to a more optional type %1",
906908
(Type, Type))

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3896,6 +3896,53 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
38963896
}
38973897
}
38983898

3899+
assert(!toType->isAny() && "casts to 'Any' should've been handled above");
3900+
assert(!toType->isAnyObject() &&
3901+
"casts to 'AnyObject' should've been handled above");
3902+
3903+
// A cast from a function type to an existential type (except `Any`)
3904+
// or an archetype type (with constraints) cannot succeed
3905+
auto toArchetypeType = toType->is<ArchetypeType>();
3906+
auto fromFunctionType = fromType->is<FunctionType>();
3907+
auto toExistentialType = toType->isAnyExistentialType();
3908+
3909+
auto toConstrainedArchetype = false;
3910+
if (toArchetypeType) {
3911+
auto archetype = toType->castTo<ArchetypeType>();
3912+
toConstrainedArchetype = archetype && !archetype->getConformsTo().empty();
3913+
}
3914+
3915+
if (fromFunctionType &&
3916+
(toExistentialType || (toArchetypeType && toConstrainedArchetype))) {
3917+
switch (contextKind) {
3918+
case CheckedCastContextKind::ConditionalCast:
3919+
case CheckedCastContextKind::ForcedCast:
3920+
diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, origToType)
3921+
.highlight(diagFromRange)
3922+
.highlight(diagToRange);
3923+
3924+
// If we're referring to a function with a return value (not Void) then
3925+
// emit a fix-it suggesting to add `()` to call the function
3926+
if (auto DRE = dyn_cast<DeclRefExpr>(fromExpr)) {
3927+
if (auto FD = dyn_cast<FuncDecl>(DRE->getDecl())) {
3928+
if (!FD->getResultInterfaceType()->isVoid()) {
3929+
diagnose(diagLoc, diag::downcast_to_unrelated_fixit, FD->getName())
3930+
.fixItInsertAfter(fromExpr->getEndLoc(), "()");
3931+
}
3932+
}
3933+
}
3934+
3935+
return CheckedCastKind::ValueCast;
3936+
break;
3937+
3938+
case CheckedCastContextKind::IsPattern:
3939+
case CheckedCastContextKind::EnumElementPattern:
3940+
case CheckedCastContextKind::IsExpr:
3941+
case CheckedCastContextKind::None:
3942+
break;
3943+
}
3944+
}
3945+
38993946
// If we can bridge through an Objective-C class, do so.
39003947
if (Type bridgedToClass = getDynamicBridgedThroughObjCClass(dc, fromType,
39013948
toType)) {

test/expr/cast/as_coerce.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,32 @@ f(1 as String) // expected-error{{cannot convert value of type 'Int' to type 'St
103103
// <rdar://problem/19650402> Swift compiler segfaults while running the annotation tests
104104
let s : AnyObject = C3()
105105
s as C3 // expected-error{{'AnyObject' is not convertible to 'C3'; did you mean to use 'as!' to force downcast?}} {{3-5=as!}}
106+
107+
// SR-6022
108+
func sr6022() -> Any { return 0 }
109+
func sr6022_1() { return; }
110+
protocol SR6022_P {}
111+
112+
_ = sr6022 as! SR6022_P // expected-warning {{cast from '() -> Any' to unrelated type 'SR6022_P' always fails}} // expected-note {{did you mean to call 'sr6022' with '()'?}}{{11-11=()}}
113+
_ = sr6022 as? SR6022_P // expected-warning {{cast from '() -> Any' to unrelated type 'SR6022_P' always fails}} // expected-note {{did you mean to call 'sr6022' with '()'}}{{11-11=()}}
114+
_ = sr6022_1 as! SR6022_P // expected-warning {{cast from '() -> ()' to unrelated type 'SR6022_P' always fails}}
115+
_ = sr6022_1 as? SR6022_P // expected-warning {{cast from '() -> ()' to unrelated type 'SR6022_P' always fails}}
116+
117+
func testSR6022_P<T: SR6022_P>(_: T.Type) {
118+
_ = sr6022 as! T // expected-warning {{cast from '() -> Any' to unrelated type 'T' always fails}} // expected-note {{did you mean to call 'sr6022' with '()'?}}{{13-13=()}}
119+
_ = sr6022 as? T // expected-warning {{cast from '() -> Any' to unrelated type 'T' always fails}} // expected-note {{did you mean to call 'sr6022' with '()'?}}{{13-13=()}}
120+
_ = sr6022_1 as! T // expected-warning {{cast from '() -> ()' to unrelated type 'T' always fails}}
121+
_ = sr6022_1 as? T // expected-warning {{cast from '() -> ()' to unrelated type 'T' always fails}}
122+
}
123+
124+
func testSR6022_P_1<U>(_: U.Type) {
125+
_ = sr6022 as! U // Okay
126+
_ = sr6022 as? U // Okay
127+
_ = sr6022_1 as! U // Okay
128+
_ = sr6022_1 as? U // Okay
129+
}
130+
131+
_ = sr6022 as! AnyObject // expected-warning {{forced cast from '() -> Any' to 'AnyObject' always succeeds; did you mean to use 'as'?}}
132+
_ = sr6022 as? AnyObject // expected-warning {{conditional cast from '() -> Any' to 'AnyObject' always succeeds}}
133+
_ = sr6022_1 as! Any // expected-warning {{forced cast from '() -> ()' to 'Any' always succeeds; did you mean to use 'as'?}}
134+
_ = sr6022_1 as? Any // expected-warning {{conditional cast from '() -> ()' to 'Any' always succeeds}}

0 commit comments

Comments
 (0)