Skip to content

Commit ac65e6f

Browse files
[Sema] Detect if we should use a conditional binding when recording the CoerceToCheckedCast fix
1 parent 7df08f8 commit ac65e6f

File tree

5 files changed

+47
-29
lines changed

5 files changed

+47
-29
lines changed

include/swift/Sema/CSFix.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,20 +1728,25 @@ class UseRawValue final : public ConstraintFix {
17281728
Type expectedType, ConstraintLocator *locator);
17291729
};
17301730

1731-
/// Replace a coercion ('as') with a forced checked cast ('as!').
1731+
/// Replace a coercion ('as') with runtime checked cast ('as!' or 'as?').
17321732
class CoerceToCheckedCast final : public ContextualMismatch {
17331733
CoerceToCheckedCast(ConstraintSystem &cs, Type fromType, Type toType,
1734-
ConstraintLocator *locator)
1734+
bool useConditionalCast, ConstraintLocator *locator)
17351735
: ContextualMismatch(cs, FixKind::CoerceToCheckedCast, fromType, toType,
1736-
locator) {}
1736+
locator),
1737+
UseConditionalCast(useConditionalCast) {}
1738+
bool UseConditionalCast = false;
17371739

17381740
public:
1739-
std::string getName() const override { return "as to as!"; }
1741+
std::string getName() const override {
1742+
return UseConditionalCast ? "as to as?" : "as to as!";
1743+
}
17401744

17411745
bool diagnose(const Solution &solution, bool asNote = false) const override;
17421746

17431747
static CoerceToCheckedCast *attempt(ConstraintSystem &cs, Type fromType,
1744-
Type toType, ConstraintLocator *locator);
1748+
Type toType, bool useConditionalCast,
1749+
ConstraintLocator *locator);
17451750
};
17461751

17471752
class RemoveInvalidCall final : public ConstraintFix {

lib/Sema/CSDiagnostics.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -964,28 +964,20 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
964964
return true;
965965
}
966966

967-
ASTNode MissingForcedDowncastFailure::getAnchor() const {
967+
ASTNode InvalidCoercionFailure::getAnchor() const {
968968
auto anchor = FailureDiagnostic::getAnchor();
969969
if (auto *assignExpr = getAsExpr<AssignExpr>(anchor))
970970
return assignExpr->getSrc();
971971
return anchor;
972972
}
973973

974-
bool MissingForcedDowncastFailure::diagnoseAsError() {
974+
bool InvalidCoercionFailure::diagnoseAsError() {
975975
auto fromType = getFromType();
976976
auto toType = getToType();
977977

978978
emitDiagnostic(diag::cannot_coerce_to_type, fromType, toType);
979979

980-
auto &solution = getSolution();
981-
auto restriction = solution.ConstraintRestrictions.find(
982-
{toType->getCanonicalType(),
983-
OptionalType::get(toType)->getCanonicalType()});
984-
// If the type has an value to optional conversion we can instead suggest
985-
// the conditional downcast as it is safer in situations like conditional
986-
// binding.
987-
if (restriction != solution.ConstraintRestrictions.end() &&
988-
restriction->getSecond() == ConversionRestrictionKind::ValueToOptional) {
980+
if (UseConditionalCast) {
989981
emitDiagnostic(diag::missing_optional_downcast)
990982
.highlight(getSourceRange())
991983
.fixItReplace(getLoc(), "as?");

lib/Sema/CSDiagnostics.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,12 +1939,15 @@ class ArgumentMismatchFailure : public ContextualFailure {
19391939
bool diagnoseMisplacedMissingArgument() const;
19401940
};
19411941

1942-
/// Replace a coercion ('as') with a forced checked cast ('as!').
1943-
class MissingForcedDowncastFailure final : public ContextualFailure {
1942+
/// Replace a coercion ('as') with a runtime checked cast ('as!' or 'as?').
1943+
class InvalidCoercionFailure final : public ContextualFailure {
1944+
bool UseConditionalCast;
1945+
19441946
public:
1945-
MissingForcedDowncastFailure(const Solution &solution, Type fromType,
1946-
Type toType, ConstraintLocator *locator)
1947-
: ContextualFailure(solution, fromType, toType, locator) {}
1947+
InvalidCoercionFailure(const Solution &solution, Type fromType, Type toType,
1948+
bool useConditionalCast, ConstraintLocator *locator)
1949+
: ContextualFailure(solution, fromType, toType, locator),
1950+
UseConditionalCast(useConditionalCast) {}
19481951

19491952
ASTNode getAnchor() const override;
19501953

lib/Sema/CSFix.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,14 @@ TreatRValueAsLValue *TreatRValueAsLValue::create(ConstraintSystem &cs,
131131

132132
bool CoerceToCheckedCast::diagnose(const Solution &solution,
133133
bool asNote) const {
134-
MissingForcedDowncastFailure failure(solution, getFromType(), getToType(),
135-
getLocator());
134+
InvalidCoercionFailure failure(solution, getFromType(), getToType(),
135+
UseConditionalCast, getLocator());
136136
return failure.diagnose(asNote);
137137
}
138138

139139
CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs,
140140
Type fromType, Type toType,
141+
bool useConditionalCast,
141142
ConstraintLocator *locator) {
142143
// If any of the types has a type variable, don't add the fix.
143144
if (fromType->hasTypeVariable() || toType->hasTypeVariable())
@@ -159,7 +160,7 @@ CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs,
159160
return nullptr;
160161

161162
return new (cs.getAllocator())
162-
CoerceToCheckedCast(cs, fromType, toType, locator);
163+
CoerceToCheckedCast(cs, fromType, toType, useConditionalCast, locator);
163164
}
164165

165166
bool TreatArrayLiteralAsDictionary::diagnose(const Solution &solution,

lib/Sema/CSSimplify.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3551,9 +3551,26 @@ bool ConstraintSystem::repairFailures(
35513551
getConstraintLocator(coercion->getSubExpr())))
35523552
return true;
35533553

3554-
// Repair a coercion ('as') with a forced checked cast ('as!').
3555-
if (auto *coerceToCheckCastFix = CoerceToCheckedCast::attempt(
3556-
*this, lhs, rhs, getConstraintLocator(locator))) {
3554+
// If the result type of the coercion has an value to optional conversion
3555+
// we can instead suggest the conditional downcast as it is safer in
3556+
// situations like conditional binding.
3557+
auto useConditionalCast = llvm::any_of(
3558+
ConstraintRestrictions,
3559+
[&](std::tuple<Type, Type, ConversionRestrictionKind> restriction) {
3560+
ConversionRestrictionKind restrictionKind;
3561+
Type type1, type2;
3562+
std::tie(type1, type2, restrictionKind) = restriction;
3563+
3564+
if (restrictionKind != ConversionRestrictionKind::ValueToOptional)
3565+
return false;
3566+
3567+
return rhs->isEqual(type1);
3568+
});
3569+
3570+
// Repair a coercion ('as') with a runtime checked cast ('as!' or 'as?').
3571+
if (auto *coerceToCheckCastFix =
3572+
CoerceToCheckedCast::attempt(*this, lhs, rhs, useConditionalCast,
3573+
getConstraintLocator(locator))) {
35573574
conversionsOrFixes.push_back(coerceToCheckCastFix);
35583575
return true;
35593576
}
@@ -9560,8 +9577,8 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
95609577
loc->isLastElement<LocatorPathElt::ApplyArgToParam>() ||
95619578
loc->isForOptionalTry()) {
95629579
if (restriction == ConversionRestrictionKind::Superclass) {
9563-
if (auto *fix =
9564-
CoerceToCheckedCast::attempt(*this, fromType, toType, loc))
9580+
if (auto *fix = CoerceToCheckedCast::attempt(
9581+
*this, fromType, toType, /*useConditionalCast*/ false, loc))
95659582
return !recordFix(fix, impact);
95669583
}
95679584

0 commit comments

Comments
 (0)