Skip to content

Commit ca8b660

Browse files
Merge pull request #39648 from LucianoPAlmeida/SR-15281-cast
[SR-15281] [Sema] Couple of contextual mismatch and runtime cast diagnostic fixes
2 parents 480bb08 + 5424797 commit ca8b660

File tree

5 files changed

+82
-12
lines changed

5 files changed

+82
-12
lines changed

include/swift/Sema/ConstraintSystem.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,10 @@ class SolutionApplicationTarget {
21812181
using RewriteTargetFn = std::function<
21822182
Optional<SolutionApplicationTarget> (SolutionApplicationTarget)>;
21832183

2184+
/// Represents a conversion restriction between two types.
2185+
using ConversionRestriction =
2186+
std::tuple<TypeBase *, TypeBase *, ConversionRestrictionKind>;
2187+
21842188
enum class ConstraintSystemPhase {
21852189
ConstraintGeneration,
21862190
Solving,
@@ -2367,8 +2371,7 @@ class ConstraintSystem {
23672371
/// there are multiple ways in which one type could convert to another, e.g.,
23682372
/// given class types A and B, the solver might choose either a superclass
23692373
/// conversion or a user-defined conversion.
2370-
std::vector<std::tuple<Type, Type, ConversionRestrictionKind>>
2371-
ConstraintRestrictions;
2374+
std::vector<ConversionRestriction> ConstraintRestrictions;
23722375

23732376
/// The set of fixes applied to make the solution work.
23742377
llvm::SmallVector<ConstraintFix *, 4> Fixes;

lib/Sema/CSDiagnostics.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -2957,6 +2957,20 @@ bool ContextualFailure::tryTypeCoercionFixIt(
29572957
if (!toType->hasTypeRepr())
29582958
return false;
29592959

2960+
// If object of the optional type is a subtype of the specified contextual
2961+
// type, let's suggest a force unwrap "!". Otherwise fallback to potential
2962+
// coercion or force cast.
2963+
if (!bothOptional && fromType->getOptionalObjectType()) {
2964+
if (TypeChecker::isSubtypeOf(fromType->lookThroughAllOptionalTypes(),
2965+
toType, getDC())) {
2966+
diagnostic.fixItInsert(
2967+
Lexer::getLocForEndOfToken(getASTContext().SourceMgr,
2968+
getSourceRange().End),
2969+
"!");
2970+
return true;
2971+
}
2972+
}
2973+
29602974
CheckedCastKind Kind =
29612975
TypeChecker::typeCheckCheckedCast(fromType, toType,
29622976
CheckedCastContextKind::None, getDC(),

lib/Sema/CSSimplify.cpp

+21-7
Original file line numberDiff line numberDiff line change
@@ -3908,9 +3908,8 @@ bool ConstraintSystem::repairFailures(
39083908
// If the result type of the coercion has an value to optional conversion
39093909
// we can instead suggest the conditional downcast as it is safer in
39103910
// situations like conditional binding.
3911-
auto useConditionalCast = llvm::any_of(
3912-
ConstraintRestrictions,
3913-
[&](std::tuple<Type, Type, ConversionRestrictionKind> restriction) {
3911+
auto useConditionalCast =
3912+
llvm::any_of(ConstraintRestrictions, [&](auto &restriction) {
39143913
ConversionRestrictionKind restrictionKind;
39153914
Type type1, type2;
39163915
std::tie(type1, type2, restrictionKind) = restriction;
@@ -6717,7 +6716,9 @@ static bool isCastToExpressibleByNilLiteral(ConstraintSystem &cs, Type fromType,
67176716
static ConstraintFix *maybeWarnAboutExtraneousCast(
67186717
ConstraintSystem &cs, Type origFromType, Type origToType, Type fromType,
67196718
Type toType, SmallVector<Type, 4> fromOptionals,
6720-
SmallVector<Type, 4> toOptionals, ConstraintSystem::TypeMatchOptions flags,
6719+
SmallVector<Type, 4> toOptionals,
6720+
const std::vector<ConversionRestriction> &constraintRestrictions,
6721+
ConstraintSystem::TypeMatchOptions flags,
67216722
ConstraintLocatorBuilder locator) {
67226723

67236724
auto last = locator.last();
@@ -6739,6 +6740,18 @@ static ConstraintFix *maybeWarnAboutExtraneousCast(
67396740
// "from" could be less optional than "to" e.g. `0 as Any?`, so
67406741
// we need to store the difference as a signed integer.
67416742
int extraOptionals = fromOptionals.size() - toOptionals.size();
6743+
6744+
// "from" expression could be a type variable with value-to-optional
6745+
// restrictions that we have to account for optionality mismatch.
6746+
const auto subExprType = cs.getType(castExpr->getSubExpr());
6747+
if (llvm::is_contained(
6748+
constraintRestrictions,
6749+
std::make_tuple(fromType.getPointer(), subExprType.getPointer(),
6750+
ConversionRestrictionKind::ValueToOptional))) {
6751+
extraOptionals++;
6752+
origFromType = OptionalType::get(origFromType);
6753+
}
6754+
67426755
// Removing the optionality from to type when the force cast expr is an IUO.
67436756
const auto *const TR = castExpr->getCastTypeRepr();
67446757
if (isExpr<ForcedCheckedCastExpr>(anchor) && TR &&
@@ -6894,7 +6907,7 @@ ConstraintSystem::simplifyCheckedCastConstraint(
68946907

68956908
if (auto *fix = maybeWarnAboutExtraneousCast(
68966909
*this, origFromType, origToType, fromType, toType, fromOptionals,
6897-
toOptionals, flags, locator)) {
6910+
toOptionals, ConstraintRestrictions, flags, locator)) {
68986911
(void)recordFix(fix);
68996912
}
69006913
};
@@ -6962,7 +6975,7 @@ ConstraintSystem::simplifyCheckedCastConstraint(
69626975
// succeed or fail.
69636976
if (auto *fix = maybeWarnAboutExtraneousCast(
69646977
*this, origFromType, origToType, fromType, toType, fromOptionals,
6965-
toOptionals, flags, locator)) {
6978+
toOptionals, ConstraintRestrictions, flags, locator)) {
69666979
(void)recordFix(fix);
69676980
}
69686981

@@ -11372,7 +11385,8 @@ ConstraintSystem::simplifyRestrictedConstraint(
1137211385
addFixConstraint(fix, matchKind, type1, type2, locator);
1137311386
}
1137411387

11375-
ConstraintRestrictions.push_back(std::make_tuple(type1, type2, restriction));
11388+
ConstraintRestrictions.push_back(
11389+
std::make_tuple(type1.getPointer(), type2.getPointer(), restriction));
1137611390
return SolutionKind::Solved;
1137711391
}
1137811392
case SolutionKind::Unsolved:

lib/Sema/CSSolver.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,10 @@ void ConstraintSystem::applySolution(const Solution &solution) {
231231
// Register constraint restrictions.
232232
// FIXME: Copy these directly into some kind of partial solution?
233233
for (auto restriction : solution.ConstraintRestrictions) {
234-
ConstraintRestrictions.push_back(
235-
std::make_tuple(restriction.first.first, restriction.first.second,
236-
restriction.second));
234+
auto &types = restriction.first;
235+
ConstraintRestrictions.push_back(std::make_tuple(types.first.getPointer(),
236+
types.second.getPointer(),
237+
restriction.second));
237238
}
238239

239240
// Register the solution's disjunction choices.

test/Constraints/casts.swift

+38
Original file line numberDiff line numberDiff line change
@@ -613,3 +613,41 @@ func decodeStringOrIntDictionary<T: FixedWidthInteger>() -> [Int: T] {
613613
fatalError()
614614
}
615615
}
616+
617+
618+
// SR-15281
619+
struct SR15281_A { }
620+
struct SR15281_B {
621+
init(a: SR15281_A) { }
622+
}
623+
624+
struct SR15281_S {
625+
var a: SR15281_A? = SR15281_A()
626+
627+
var b: SR15281_B {
628+
a.flatMap(SR15281_B.init(a:)) // expected-error{{cannot convert return expression of type 'SR15281_B?' to return type 'SR15281_B'}} {{34-34=!}}
629+
}
630+
631+
var b1: SR15281_B {
632+
a.flatMap(SR15281_B.init(a:)) as! SR15281_B
633+
// expected-warning@-1 {{forced cast from 'SR15281_B?' to 'SR15281_B' only unwraps optionals; did you mean to use '!'?}} {{34-34=!}} {{34-48=}}
634+
}
635+
}
636+
637+
class SR15281_AC {}
638+
class SR15281_BC {
639+
init(a: SR15281_AC) { }
640+
}
641+
class SR15281_CC: SR15281_BC {}
642+
643+
struct SR15281_SC {
644+
var a: SR15281_AC? = SR15281_AC()
645+
646+
var b: SR15281_BC {
647+
a.flatMap(SR15281_BC.init(a:)) // expected-error{{cannot convert return expression of type 'SR15281_BC?' to return type 'SR15281_BC'}} {{35-35=!}}
648+
}
649+
650+
var c: SR15281_BC {
651+
a.flatMap(SR15281_CC.init(a:)) // expected-error{{cannot convert return expression of type 'SR15281_CC?' to return type 'SR15281_BC'}} {{35-35=!}}
652+
}
653+
}

0 commit comments

Comments
 (0)