Skip to content

Commit 99f1704

Browse files
[CSFix] Create a fix for checked cast that always fail and existential to CFType
1 parent 11c3ff0 commit 99f1704

File tree

4 files changed

+174
-9
lines changed

4 files changed

+174
-9
lines changed

include/swift/Sema/CSFix.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,19 @@ enum class FixKind : uint8_t {
316316
/// succeed.
317317
AllowNoopCheckedCast,
318318

319+
/// Warn about special runtime case where statically known
320+
/// checked cast from existentials to CFType always succeed.
321+
AllowNoopExistentialToCFTypeCheckedCast,
322+
319323
/// Allow a runtime checked cast where at compile time the from is
320324
/// convertible, but runtime does not support such convertions. e.g.
321325
/// function type casts.
322326
AllowUnsupportedRuntimeCheckedCast,
323327

328+
/// Allow a runtime checked cast where it is known at compile time
329+
/// always fails.
330+
AllowCheckedCastToUnrelated,
331+
324332
/// Allow reference to a static member on a protocol metatype
325333
/// even though result type of the reference doesn't conform
326334
/// to an expected protocol.
@@ -2789,6 +2797,31 @@ class AllowNoopCheckedCast final : public CheckedCastContextualMismatchWarning {
27892797
}
27902798
};
27912799

2800+
class AllowNoopExistentialToCFTypeCheckedCast final
2801+
: public CheckedCastContextualMismatchWarning {
2802+
AllowNoopExistentialToCFTypeCheckedCast(ConstraintSystem &cs, Type fromType,
2803+
Type toType, CheckedCastKind kind,
2804+
ConstraintLocator *locator)
2805+
: CheckedCastContextualMismatchWarning(
2806+
cs, FixKind::AllowNoopExistentialToCFTypeCheckedCast, fromType,
2807+
toType, kind, locator) {}
2808+
2809+
public:
2810+
std::string getName() const override {
2811+
return "checked cast from existential to CFType always succeeds";
2812+
}
2813+
2814+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2815+
2816+
static AllowNoopExistentialToCFTypeCheckedCast *
2817+
attempt(ConstraintSystem &cs, Type fromType, Type toType,
2818+
CheckedCastKind kind, ConstraintLocator *locator);
2819+
2820+
static bool classof(ConstraintFix *fix) {
2821+
return fix->getKind() == FixKind::AllowNoopExistentialToCFTypeCheckedCast;
2822+
}
2823+
};
2824+
27922825
class AllowUnsupportedRuntimeCheckedCast final
27932826
: public CheckedCastContextualMismatchWarning {
27942827
AllowUnsupportedRuntimeCheckedCast(ConstraintSystem &cs, Type fromType,
@@ -2816,6 +2849,29 @@ class AllowUnsupportedRuntimeCheckedCast final
28162849
}
28172850
};
28182851

2852+
class AllowCheckedCastToUnrelated final
2853+
: public CheckedCastContextualMismatchWarning {
2854+
AllowCheckedCastToUnrelated(ConstraintSystem &cs, Type fromType, Type toType,
2855+
CheckedCastKind kind, ConstraintLocator *locator)
2856+
: CheckedCastContextualMismatchWarning(
2857+
cs, FixKind::AllowCheckedCastToUnrelated, fromType, toType, kind,
2858+
locator) {}
2859+
2860+
public:
2861+
std::string getName() const override { return "checked cast always fails"; }
2862+
2863+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2864+
2865+
static AllowCheckedCastToUnrelated *attempt(ConstraintSystem &cs,
2866+
Type fromType, Type toType,
2867+
CheckedCastKind kind,
2868+
ConstraintLocator *locator);
2869+
2870+
static bool classof(ConstraintFix *fix) {
2871+
return fix->getKind() == FixKind::AllowCheckedCastToUnrelated;
2872+
}
2873+
};
2874+
28192875
class AllowInvalidStaticMemberRefOnProtocolMetatype final
28202876
: public ConstraintFix {
28212877
AllowInvalidStaticMemberRefOnProtocolMetatype(ConstraintSystem &cs,

lib/Sema/CSDiagnostics.cpp

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3035,10 +3035,8 @@ bool ContextualFailure::tryTypeCoercionFixIt(
30353035
}
30363036
}
30373037

3038-
CheckedCastKind Kind =
3039-
TypeChecker::typeCheckCheckedCast(fromType, toType,
3040-
CheckedCastContextKind::None, getDC(),
3041-
SourceLoc(), nullptr, SourceRange());
3038+
CheckedCastKind Kind = TypeChecker::typeCheckCheckedCast(
3039+
fromType, toType, CheckedCastContextKind::None, getDC());
30423040

30433041
if (Kind != CheckedCastKind::Unresolved) {
30443042
bool canUseAs = Kind == CheckedCastKind::Coercion ||
@@ -7982,7 +7980,7 @@ bool CoercibleOptionalCheckedCastFailure::diagnoseConditionalCastExpr() const {
79827980
return true;
79837981
}
79847982

7985-
bool NoopCheckedCast::diagnoseIfExpr() const {
7983+
bool NoopCheckedCast::diagnoseIsExpr() const {
79867984
auto *expr = getAsExpr<IsExpr>(CastExpr);
79877985
if (!expr)
79887986
return false;
@@ -8028,7 +8026,7 @@ bool NoopCheckedCast::diagnoseForcedCastExpr() const {
80288026
}
80298027

80308028
bool NoopCheckedCast::diagnoseAsError() {
8031-
if (diagnoseIfExpr())
8029+
if (diagnoseIsExpr())
80328030
return true;
80338031

80348032
if (diagnoseForcedCastExpr())
@@ -8040,6 +8038,11 @@ bool NoopCheckedCast::diagnoseAsError() {
80408038
llvm_unreachable("Shouldn't reach here");
80418039
}
80428040

8041+
bool NoopExistentialToCFTypeCheckedCast::diagnoseAsError() {
8042+
emitDiagnostic(diag::isa_is_foreign_check, getToType());
8043+
return true;
8044+
}
8045+
80438046
bool CoercibleOptionalCheckedCastFailure::diagnoseAsError() {
80448047
if (diagnoseIfExpr())
80458048
return true;
@@ -8062,6 +8065,42 @@ bool UnsupportedRuntimeCheckedCastFailure::diagnoseAsError() {
80628065
return true;
80638066
}
80648067

8068+
bool CheckedCastToUnrelatedFailure::diagnoseAsError() {
8069+
const auto toType = getToType();
8070+
auto *sub = CastExpr->getSubExpr()->getSemanticsProvidingExpr();
8071+
// FIXME: This literal diagnostics needs to be revisited by a proposal
8072+
// to unify casting semantics for literals.
8073+
// https://bugs.swift.org/browse/SR-12093
8074+
auto &ctx = getASTContext();
8075+
auto *dc = getDC();
8076+
if (isa<LiteralExpr>(sub)) {
8077+
auto *protocol = TypeChecker::getLiteralProtocol(ctx, sub);
8078+
// Special handle for literals conditional checked cast when they can
8079+
// be statically coerced to the cast type.
8080+
if (protocol && TypeChecker::conformsToProtocol(toType, protocol,
8081+
dc->getParentModule())) {
8082+
emitDiagnostic(diag::literal_conditional_downcast_to_coercion, toType);
8083+
return true;
8084+
}
8085+
}
8086+
8087+
emitDiagnostic(diag::downcast_to_unrelated, getFromType(), toType)
8088+
.highlight(getFromRange())
8089+
.highlight(getToRange());
8090+
// If we're referring to a function with a return value (not Void) then
8091+
// emit a fix-it suggesting to add `()` to call the function
8092+
if (auto DRE = dyn_cast<DeclRefExpr>(sub)) {
8093+
if (auto FD = dyn_cast<FuncDecl>(DRE->getDecl())) {
8094+
if (!FD->getResultInterfaceType()->isVoid()) {
8095+
emitDiagnostic(diag::downcast_to_unrelated_fixit,
8096+
FD->getBaseIdentifier())
8097+
.fixItInsertAfter(sub->getEndLoc(), "()");
8098+
}
8099+
}
8100+
}
8101+
return true;
8102+
}
8103+
80658104
bool InvalidWeakAttributeUse::diagnoseAsError() {
80668105
auto *pattern =
80678106
dyn_cast_or_null<NamedPattern>(getAnchor().dyn_cast<Pattern *>());

lib/Sema/CSDiagnostics.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2574,13 +2574,25 @@ class NoopCheckedCast final : public CheckedCastBaseFailure {
25742574
bool diagnoseAsError() override;
25752575

25762576
private:
2577-
bool diagnoseIfExpr() const;
2577+
bool diagnoseIsExpr() const;
25782578

25792579
bool diagnoseForcedCastExpr() const;
25802580

25812581
bool diagnoseConditionalCastExpr() const;
25822582
};
25832583

2584+
/// Warn situations where the compiler can statically know a runtime
2585+
/// checked cast always succeed.
2586+
class NoopExistentialToCFTypeCheckedCast final : public CheckedCastBaseFailure {
2587+
public:
2588+
NoopExistentialToCFTypeCheckedCast(const Solution &solution, Type fromType,
2589+
Type toType, CheckedCastKind kind,
2590+
ConstraintLocator *locator)
2591+
: CheckedCastBaseFailure(solution, fromType, toType, kind, locator) {}
2592+
2593+
bool diagnoseAsError() override;
2594+
};
2595+
25842596
/// Warn situations where the compiler can statically know a runtime
25852597
/// check is not supported.
25862598
class UnsupportedRuntimeCheckedCastFailure final
@@ -2594,6 +2606,18 @@ class UnsupportedRuntimeCheckedCastFailure final
25942606
bool diagnoseAsError() override;
25952607
};
25962608

2609+
/// Emit a warning when compiler can detect that checked cast would fail at
2610+
/// runtime based on statically known types.
2611+
class CheckedCastToUnrelatedFailure final : public CheckedCastBaseFailure {
2612+
public:
2613+
CheckedCastToUnrelatedFailure(const Solution &solution, Type fromType,
2614+
Type toType, CheckedCastKind kind,
2615+
ConstraintLocator *locator)
2616+
: CheckedCastBaseFailure(solution, fromType, toType, kind, locator) {}
2617+
2618+
bool diagnoseAsError() override;
2619+
};
2620+
25972621
/// Diagnose situations when static member reference has invalid result
25982622
/// type which disqualifies it from being used on a protocol metatype base.
25992623
///

lib/Sema/CSFix.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs,
152152
return nullptr;
153153

154154
const auto castKind = TypeChecker::typeCheckCheckedCast(
155-
fromType, toType, CheckedCastContextKind::Coercion, cs.DC,
156-
SourceLoc(), coerceExpr->getSubExpr(), SourceRange());
155+
fromType, toType, CheckedCastContextKind::Coercion, cs.DC);
157156

158157
// Invalid cast.
159158
if (castKind == CheckedCastKind::Unresolved)
@@ -1999,6 +1998,33 @@ bool AllowNoopCheckedCast::diagnose(const Solution &solution,
19991998
return warning.diagnose(asNote);
20001999
}
20012000

2001+
AllowNoopExistentialToCFTypeCheckedCast *
2002+
AllowNoopExistentialToCFTypeCheckedCast::attempt(ConstraintSystem &cs,
2003+
Type fromType, Type toType,
2004+
CheckedCastKind kind,
2005+
ConstraintLocator *locator) {
2006+
if (!isExpr<IsExpr>(locator->getAnchor()))
2007+
return nullptr;
2008+
2009+
if (!fromType->isExistentialType())
2010+
return nullptr;
2011+
2012+
const auto *cls = toType->getAs<ClassType>();
2013+
if (!(cls && cls->getDecl()->getForeignClassKind() ==
2014+
ClassDecl::ForeignKind::CFType))
2015+
return nullptr;
2016+
2017+
return new (cs.getAllocator()) AllowNoopExistentialToCFTypeCheckedCast(
2018+
cs, fromType, toType, kind, locator);
2019+
}
2020+
2021+
bool AllowNoopExistentialToCFTypeCheckedCast::diagnose(const Solution &solution,
2022+
bool asNote) const {
2023+
NoopExistentialToCFTypeCheckedCast warning(
2024+
solution, getFromType(), getToType(), CastKind, getLocator());
2025+
return warning.diagnose(asNote);
2026+
}
2027+
20022028
// Although function types maybe compile-time convertible because
20032029
// compiler can emit thunks at SIL to handle the conversion when
20042030
// required, only convertions that are supported by the runtime are
@@ -2041,6 +2067,26 @@ bool AllowUnsupportedRuntimeCheckedCast::diagnose(const Solution &solution,
20412067
return failure.diagnose(asNote);
20422068
}
20432069

2070+
AllowCheckedCastToUnrelated *
2071+
AllowCheckedCastToUnrelated::attempt(ConstraintSystem &cs, Type fromType,
2072+
Type toType, CheckedCastKind kind,
2073+
ConstraintLocator *locator) {
2074+
// Explicit optional-to-optional casts always succeed because a nil
2075+
// value of any optional type can be cast to any other optional type.
2076+
if (fromType->getOptionalObjectType() && toType->getOptionalObjectType()) {
2077+
return nullptr;
2078+
}
2079+
return new (cs.getAllocator())
2080+
AllowCheckedCastToUnrelated(cs, fromType, toType, kind, locator);
2081+
}
2082+
2083+
bool AllowCheckedCastToUnrelated::diagnose(const Solution &solution,
2084+
bool asNote) const {
2085+
CheckedCastToUnrelatedFailure warning(solution, getFromType(), getToType(),
2086+
CastKind, getLocator());
2087+
return warning.diagnose(asNote);
2088+
}
2089+
20442090
bool AllowInvalidStaticMemberRefOnProtocolMetatype::diagnose(
20452091
const Solution &solution, bool asNote) const {
20462092
InvalidMemberRefOnProtocolMetatype failure(solution, getLocator());

0 commit comments

Comments
 (0)