Skip to content

Commit a716d40

Browse files
authored
Merge pull request #6774 from jckarter/nsnumber-conditional-casting
2 parents 9586a23 + c03371a commit a716d40

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
@@ -810,6 +810,10 @@ ERROR(missing_unwrap_optional_try,none,
810810
ERROR(missing_forced_downcast,none,
811811
"%0 is not convertible to %1; "
812812
"did you mean to use 'as!' to force downcast?", (Type, Type))
813+
WARNING(missing_forced_downcast_swift3_compat_warning,none,
814+
"bridging %0 to %1 may fail at runtime and will become a checked "
815+
"cast in Swift 4; did you mean to use 'as!' to force downcast?",
816+
(Type, Type))
813817
ERROR(missing_explicit_conversion,none,
814818
"%0 is not implicitly convertible to %1; "
815819
"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
@@ -4168,8 +4168,10 @@ StringRef swift::getCheckedCastKindName(CheckedCastKind kind) {
41684168
return "dictionary_downcast";
41694169
case CheckedCastKind::SetDowncast:
41704170
return "set_downcast";
4171-
case CheckedCastKind::BridgingCast:
4172-
return "bridging_cast";
4171+
case CheckedCastKind::BridgingCoercion:
4172+
return "bridging_coercion";
4173+
case CheckedCastKind::Swift3BridgingDowncast:
4174+
return "bridging_downcast";
41734175
}
41744176
llvm_unreachable("bad checked cast name");
41754177
}

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:
@@ -7013,17 +7030,35 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
70137030
fromType, toType, CheckedCastContextKind::None, DC,
70147031
coerceExpr->getLoc(), subExpr,
70157032
coerceExpr->getCastTypeLoc().getSourceRange());
7016-
7033+
70177034
switch (castKind) {
70187035
// Invalid cast.
70197036
case CheckedCastKind::Unresolved:
70207037
// Fix didn't work, let diagnoseFailureForExpr handle this.
70217038
return false;
70227039
case CheckedCastKind::Coercion:
7023-
case CheckedCastKind::BridgingCast:
7040+
case CheckedCastKind::BridgingCoercion:
70247041
llvm_unreachable("Coercions handled in other disjunction branch");
70257042

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

lib/Sema/CSDiag.cpp

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

lib/Sema/CSSimplify.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2421,15 +2421,15 @@ ConstraintSystem::simplifyCheckedCastConstraint(
24212421
};
24222422

24232423
do {
2424-
// Dig out the fixed type to which this type refers.
2424+
// Dig out the fixed type this type refers to.
24252425
fromType = getFixedTypeRecursive(fromType, flags, /*wantRValue=*/true);
24262426

24272427
// If we hit a type variable without a fixed type, we can't
24282428
// solve this yet.
24292429
if (fromType->isTypeVariableOrMember())
24302430
return formUnsolved();
24312431

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

24352435
// If we hit a type variable without a fixed type, we can't
@@ -2519,7 +2519,8 @@ ConstraintSystem::simplifyCheckedCastConstraint(
25192519
}
25202520

25212521
case CheckedCastKind::Coercion:
2522-
case CheckedCastKind::BridgingCast:
2522+
case CheckedCastKind::BridgingCoercion:
2523+
case CheckedCastKind::Swift3BridgingDowncast:
25232524
case CheckedCastKind::Unresolved:
25242525
llvm_unreachable("Not a valid result");
25252526
}
@@ -3404,6 +3405,15 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1,
34043405
Type bridgedValueType;
34053406
if (auto objcClass = TC.Context.getBridgedToObjC(DC, unwrappedToType,
34063407
&bridgedValueType)) {
3408+
// Bridging NSNumber to NSValue is one-way, since there are multiple Swift
3409+
// value types that bridge to those object types. It requires a checked
3410+
// cast to get back.
3411+
// We accepted these coercions in Swift 3 mode, so we have to live with
3412+
// them (but give a warning) in that language mode.
3413+
if (!TC.Context.LangOpts.isSwiftVersion3()
3414+
&& TC.isObjCClassWithMultipleSwiftBridgedTypes(objcClass, DC))
3415+
return SolutionKind::Error;
3416+
34073417
// If the bridged value type is generic, the generic arguments
34083418
// must either match or be bridged.
34093419
// FIXME: This should be an associated type of the protocol.

0 commit comments

Comments
 (0)