Skip to content

Commit c03371a

Browse files
committed
Sema: NSValue-to-value-type casts are failable and should be checked.
In Swift 4 mode, no longer consider e.g. 'nsNumber as Int' or 'nsValue as NSRange' to be valid coercions. This would break compatibility with Swift 3, so in Swift 3 mode, accept the coercion, but *also* accept a checked cast without a warning, and raise a migration warning about the unchecked coercion.
1 parent 5c0afe9 commit c03371a

15 files changed

+546
-140
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,10 @@ ERROR(missing_unwrap_optional_try,none,
812812
ERROR(missing_forced_downcast,none,
813813
"%0 is not convertible to %1; "
814814
"did you mean to use 'as!' to force downcast?", (Type, Type))
815+
WARNING(missing_forced_downcast_swift3_compat_warning,none,
816+
"bridging %0 to %1 may fail at runtime and will become a checked "
817+
"cast in Swift 4; did you mean to use 'as!' to force downcast?",
818+
(Type, Type))
815819
ERROR(missing_explicit_conversion,none,
816820
"%0 is not implicitly convertible to %1; "
817821
"did you mean to use 'as' to explicitly convert?", (Type, Type))

include/swift/AST/Expr.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ enum class ExprKind : uint8_t {
6262
#include "swift/AST/ExprNodes.def"
6363
};
6464

65-
/// Discriminates the different kinds of checked cast supported.
65+
/// Discriminates certain kinds of checked cast that have specialized diagnostic
66+
/// and/or code generation peephole behavior.
6667
///
67-
/// This enumeration should not exist. Only the collection downcast kinds are
68-
/// currently significant. Please don't add new kinds.
68+
/// This enumeration should not have any semantic effect on the behavior of a
69+
/// well-typed program, since the runtime can perform all casts that are
70+
/// statically accepted.
6971
enum class CheckedCastKind : unsigned {
7072
/// The kind has not been determined yet.
7173
Unresolved,
@@ -75,18 +77,25 @@ enum class CheckedCastKind : unsigned {
7577

7678
/// The requested cast is an implicit conversion, so this is a coercion.
7779
Coercion = First_Resolved,
78-
/// A non-value-changing checked cast.
80+
/// A checked cast with no known specific behavior.
7981
ValueCast,
8082
// A downcast from an array type to another array type.
8183
ArrayDowncast,
8284
// A downcast from a dictionary type to another dictionary type.
8385
DictionaryDowncast,
8486
// A downcast from a set type to another set type.
8587
SetDowncast,
86-
/// A bridging cast.
87-
BridgingCast,
88+
/// A bridging conversion that always succeeds.
89+
BridgingCoercion,
90+
/// A bridging conversion that may fail, because there are multiple Swift
91+
/// value types that bridge to the same Cocoa object type.
92+
///
93+
/// This kind is only used for Swift 3 compatibility diagnostics and is
94+
/// treated the same as 'BridgingCoercion' otherwise. In Swift 4 or later,
95+
/// any conversions with this kind show up as ValueCasts.
96+
Swift3BridgingDowncast,
8897

89-
Last_CheckedCastKind = BridgingCast,
98+
Last_CheckedCastKind = Swift3BridgingDowncast,
9099
};
91100

92101
enum class AccessSemantics : unsigned char {

include/swift/AST/KnownFoundationEntities.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ FOUNDATION_ENTITY(NSSet)
3232
FOUNDATION_ENTITY(NSString)
3333
FOUNDATION_ENTITY(NSUInteger)
3434
FOUNDATION_ENTITY(NSURL)
35+
FOUNDATION_ENTITY(NSValue)
3536
FOUNDATION_ENTITY(NSZone)
3637

3738
#undef FOUNDATION_ENTITY

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4114,8 +4114,10 @@ StringRef swift::getCheckedCastKindName(CheckedCastKind kind) {
41144114
return "dictionary_downcast";
41154115
case CheckedCastKind::SetDowncast:
41164116
return "set_downcast";
4117-
case CheckedCastKind::BridgingCast:
4118-
return "bridging_cast";
4117+
case CheckedCastKind::BridgingCoercion:
4118+
return "bridging_coercion";
4119+
case CheckedCastKind::Swift3BridgingDowncast:
4120+
return "bridging_downcast";
41194121
}
41204122
llvm_unreachable("bad checked cast name");
41214123
}

lib/AST/Pattern.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ bool Pattern::isRefutablePattern() const {
298298
// If this is an always matching 'is' pattern, then it isn't refutable.
299299
if (auto *is = dyn_cast<IsPattern>(Node))
300300
if (is->getCastKind() == CheckedCastKind::Coercion ||
301-
is->getCastKind() == CheckedCastKind::BridgingCast)
301+
is->getCastKind() == CheckedCastKind::BridgingCoercion)
302302
return;
303303

304304
// If this is an ExprPattern that isn't resolved yet, do some simple

lib/Sema/CSApply.cpp

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2973,7 +2973,7 @@ namespace {
29732973
break;
29742974

29752975
case CheckedCastKind::Coercion:
2976-
case CheckedCastKind::BridgingCast:
2976+
case CheckedCastKind::BridgingCoercion:
29772977
// Check is trivially true.
29782978
tc.diagnose(expr->getLoc(), diag::isa_is_always_true, "is");
29792979
expr->setCastKind(castKind);
@@ -2988,6 +2988,7 @@ namespace {
29882988
}
29892989
expr->setCastKind(castKind);
29902990
break;
2991+
case CheckedCastKind::Swift3BridgingDowncast:
29912992
case CheckedCastKind::ArrayDowncast:
29922993
case CheckedCastKind::DictionaryDowncast:
29932994
case CheckedCastKind::SetDowncast:
@@ -3296,6 +3297,20 @@ namespace {
32963297

32973298
Expr *sub = expr->getSubExpr();
32983299
Type toInstanceType = toType->lookThroughAllAnyOptionalTypes();
3300+
3301+
// Warn about NSNumber and NSValue bridging coercions we accepted in
3302+
// Swift 3 but which can fail at runtime.
3303+
if (tc.Context.LangOpts.isSwiftVersion3()
3304+
&& tc.typeCheckCheckedCast(sub->getType(), toInstanceType,
3305+
CheckedCastContextKind::None,
3306+
dc, SourceLoc(), sub, SourceRange())
3307+
== CheckedCastKind::Swift3BridgingDowncast) {
3308+
tc.diagnose(expr->getLoc(),
3309+
diag::missing_forced_downcast_swift3_compat_warning,
3310+
sub->getType(), toInstanceType)
3311+
.fixItReplace(expr->getAsLoc(), "as!");
3312+
}
3313+
32993314
sub = buildObjCBridgeExpr(sub, toInstanceType, locator);
33003315
if (!sub) return nullptr;
33013316
expr->setSubExpr(sub);
@@ -3329,7 +3344,7 @@ namespace {
33293344
case CheckedCastKind::Unresolved:
33303345
return nullptr;
33313346
case CheckedCastKind::Coercion:
3332-
case CheckedCastKind::BridgingCast: {
3347+
case CheckedCastKind::BridgingCoercion: {
33333348
if (SuppressDiagnostics)
33343349
return nullptr;
33353350

@@ -3355,6 +3370,7 @@ namespace {
33553370
}
33563371

33573372
// Valid casts.
3373+
case CheckedCastKind::Swift3BridgingDowncast:
33583374
case CheckedCastKind::ArrayDowncast:
33593375
case CheckedCastKind::DictionaryDowncast:
33603376
case CheckedCastKind::SetDowncast:
@@ -3399,7 +3415,7 @@ namespace {
33993415
break;
34003416

34013417
case CheckedCastKind::Coercion:
3402-
case CheckedCastKind::BridgingCast: {
3418+
case CheckedCastKind::BridgingCoercion: {
34033419
if (SuppressDiagnostics)
34043420
return nullptr;
34053421

@@ -3427,6 +3443,7 @@ namespace {
34273443
}
34283444

34293445
// Valid casts.
3446+
case CheckedCastKind::Swift3BridgingDowncast:
34303447
case CheckedCastKind::ArrayDowncast:
34313448
case CheckedCastKind::DictionaryDowncast:
34323449
case CheckedCastKind::SetDowncast:
@@ -7015,17 +7032,35 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
70157032
fromType, toType, CheckedCastContextKind::None, DC,
70167033
coerceExpr->getLoc(), subExpr,
70177034
coerceExpr->getCastTypeLoc().getSourceRange());
7018-
7035+
70197036
switch (castKind) {
70207037
// Invalid cast.
70217038
case CheckedCastKind::Unresolved:
70227039
// Fix didn't work, let diagnoseFailureForExpr handle this.
70237040
return false;
70247041
case CheckedCastKind::Coercion:
7025-
case CheckedCastKind::BridgingCast:
7042+
case CheckedCastKind::BridgingCoercion:
70267043
llvm_unreachable("Coercions handled in other disjunction branch");
70277044

70287045
// Valid casts.
7046+
case CheckedCastKind::Swift3BridgingDowncast: {
7047+
// Swift 3 accepted coercions from NSNumber and NSValue to Swift
7048+
// value types, even though there are multiple Swift types that
7049+
// bridge to those classes, and the bridging operation back into Swift
7050+
// is type-checked. For compatibility, downgrade to a warning.
7051+
assert(TC.Context.LangOpts.isSwiftVersion3()
7052+
&& "should only appear in Swift 3 compat mode");
7053+
7054+
TC.diagnose(coerceExpr->getLoc(),
7055+
diag::missing_forced_downcast_swift3_compat_warning,
7056+
fromType, toType)
7057+
.highlight(coerceExpr->getSourceRange())
7058+
.fixItReplace(coerceExpr->getLoc(), "as!");
7059+
7060+
// This is just a warning, so allow the expression to type-check.
7061+
return false;
7062+
}
7063+
70297064
case CheckedCastKind::ArrayDowncast:
70307065
case CheckedCastKind::DictionaryDowncast:
70317066
case CheckedCastKind::SetDowncast:

lib/Sema/CSDiag.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3890,7 +3890,7 @@ addTypeCoerceFixit(InFlightDiagnostic &diag, ConstraintSystem *CS,
38903890
llvm::raw_svector_ostream OS(buffer);
38913891
toType->print(OS);
38923892
bool canUseAs = Kind == CheckedCastKind::Coercion ||
3893-
Kind == CheckedCastKind::BridgingCast;
3893+
Kind == CheckedCastKind::BridgingCoercion;
38943894
diag.fixItInsert(Lexer::getLocForEndOfToken(CS->DC->getASTContext().SourceMgr,
38953895
expr->getEndLoc()),
38963896
(llvm::Twine(canUseAs ? " as " : " as! ") +

lib/Sema/CSSimplify.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,15 +2414,15 @@ ConstraintSystem::simplifyCheckedCastConstraint(
24142414
};
24152415

24162416
do {
2417-
// Dig out the fixed type to which this type refers.
2417+
// Dig out the fixed type this type refers to.
24182418
fromType = getFixedTypeRecursive(fromType, flags, /*wantRValue=*/true);
24192419

24202420
// If we hit a type variable without a fixed type, we can't
24212421
// solve this yet.
24222422
if (fromType->isTypeVariableOrMember())
24232423
return formUnsolved();
24242424

2425-
// Dig out the fixed type to which this type refers.
2425+
// Dig out the fixed type this type refers to.
24262426
toType = getFixedTypeRecursive(toType, flags, /*wantRValue=*/true);
24272427

24282428
// If we hit a type variable without a fixed type, we can't
@@ -2512,7 +2512,8 @@ ConstraintSystem::simplifyCheckedCastConstraint(
25122512
}
25132513

25142514
case CheckedCastKind::Coercion:
2515-
case CheckedCastKind::BridgingCast:
2515+
case CheckedCastKind::BridgingCoercion:
2516+
case CheckedCastKind::Swift3BridgingDowncast:
25162517
case CheckedCastKind::Unresolved:
25172518
llvm_unreachable("Not a valid result");
25182519
}
@@ -3397,6 +3398,15 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1,
33973398
Type bridgedValueType;
33983399
if (auto objcClass = TC.Context.getBridgedToObjC(DC, unwrappedToType,
33993400
&bridgedValueType)) {
3401+
// Bridging NSNumber to NSValue is one-way, since there are multiple Swift
3402+
// value types that bridge to those object types. It requires a checked
3403+
// cast to get back.
3404+
// We accepted these coercions in Swift 3 mode, so we have to live with
3405+
// them (but give a warning) in that language mode.
3406+
if (!TC.Context.LangOpts.isSwiftVersion3()
3407+
&& TC.isObjCClassWithMultipleSwiftBridgedTypes(objcClass, DC))
3408+
return SolutionKind::Error;
3409+
34003410
// If the bridged value type is generic, the generic arguments
34013411
// must either match or be bridged.
34023412
// FIXME: This should be an associated type of the protocol.

0 commit comments

Comments
 (0)