diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 1bfc2c84fae..59b25822679 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -94,8 +94,11 @@ enum class CheckedCastKind : unsigned { // A downcast from a dictionary type to another dictionary type that // requires bridging. DictionaryDowncastBridged, - - Last_CheckedCastKind = DictionaryDowncastBridged, + /// A downcast from an object of class or Objective-C existential + /// type to its bridged value type. + BridgeFromObjectiveC, + + Last_CheckedCastKind = BridgeFromObjectiveC, }; /// Expr - Base class for all expressions in swift. diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index aa246c5e0bf..3c91a25bc94 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2447,6 +2447,8 @@ StringRef swift::getCheckedCastKindName(CheckedCastKind kind) { return "dictionary_downcast"; case CheckedCastKind::DictionaryDowncastBridged: return "dictionary_downcast_bridged"; + case CheckedCastKind::BridgeFromObjectiveC: + return "bridge_from_objc"; } llvm_unreachable("bad checked cast name"); } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 9dcff7572f4..1005f4aeea1 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1221,21 +1221,6 @@ namespace { return bridgeFromObjectiveC(object, valueType, false); } - /// Conditionally bridge the given object from Objective-C to its - /// value type. - /// - /// This routine should only be used for bridging value types. - /// - /// \param object The object, whose type should already be of the type - /// that the value type bridges through. - /// - /// \param valueType The value type to which we are bridging. - /// - /// \returns a value of type \c valueType? that stores the bridged result. - Expr *conditionallyBridgeFromObjectiveC(Expr *object, Type valueType) { - return bridgeFromObjectiveC(object, valueType, true); - } - TypeAliasDecl *MaxIntegerTypeDecl = nullptr; TypeAliasDecl *MaxFloatTypeDecl = nullptr; @@ -2386,6 +2371,7 @@ namespace { case CheckedCastKind::ExistentialToConcrete: case CheckedCastKind::ConcreteToArchetype: case CheckedCastKind::ConcreteToUnrelatedExistential: + case CheckedCastKind::BridgeFromObjectiveC: // Valid checks. expr->setCastKind(castKind); break; @@ -2399,19 +2385,17 @@ namespace { // Dig through the optionals in the from/to types. SmallVector fromOptionals; - auto fromValueType = plumbOptionals(fromType, fromOptionals); + plumbOptionals(fromType, fromOptionals); SmallVector toOptionals; - auto toValueType = plumbOptionals(toType, toOptionals); + plumbOptionals(toType, toOptionals); - // If we have an imbalance of optionals, a collection downcast, or - // are bridging through an Objective-C class, handle this as a - // checked cast followed by a getLogicValue. + // If we have an imbalance of optionals or a collection + // downcast, handle this as a checked cast followed by a + // a 'hasValue' check. if (fromOptionals.size() != toOptionals.size() || castKind == CheckedCastKind::ArrayDowncast || castKind == CheckedCastKind::DictionaryDowncast || - tc.getDynamicBridgedThroughObjCClass(cs.DC, - fromValueType, - toValueType)) { + castKind == CheckedCastKind::DictionaryDowncastBridged) { auto toOptType = OptionalType::get(toType); ConditionalCheckedCastExpr *cast = new (tc.Context) ConditionalCheckedCastExpr( @@ -2455,14 +2439,9 @@ namespace { SmallVector destOptionals; auto destValueType = plumbOptionals(finalResultType, destOptionals); - // Check whether we need to bridge the source type to the - // destination type. - Type bridgedThroughClass - = tc.getDynamicBridgedThroughObjCClass(cs.DC, srcType, destValueType); - // There's nothing special to do if the operand isn't optional // and we don't need any bridging. - if (srcOptionals.empty() && !bridgedThroughClass) { + if (srcOptionals.empty()) { cast->setType(finalResultType); return cast; } @@ -2474,11 +2453,8 @@ namespace { assert(!destOptionals.empty() && "result of checked cast is not an optional type"); cast->setType(destOptionals.back()); - - if (bridgedThroughClass) - cast->setType(OptionalType::get(bridgedThroughClass)); } else { - cast->setType(bridgedThroughClass? bridgedThroughClass : destValueType); + cast->setType(destValueType); } // The result type (without the final optional) is a subtype of @@ -2492,14 +2468,10 @@ namespace { unsigned numRequiredOptionals = srcOptionals.size() - (destOptionals.size() - destExtraOptionals); - // Determine whether we require conditional bridging. - bool requiresConditionalBridging = conditionalCast && bridgedThroughClass; - // The number of OptionalEvaluationExprs between the point of the // inner cast and the enclosing OptionalEvaluationExpr (exclusive) // which represents failure for the entire operation. - unsigned failureDepth = destOptionals.size() - destExtraOptionals - + requiresConditionalBridging; + unsigned failureDepth = destOptionals.size() - destExtraOptionals; // Drill down on the operand until it's non-optional. SourceLoc fakeQuestionLoc = subExpr->getEndLoc(); @@ -2509,7 +2481,7 @@ namespace { // As we move into the range of mapped optionals, start // lowering the depth. - unsigned depth = failureDepth - requiresConditionalBridging; + unsigned depth = failureDepth; if (i >= numRequiredOptionals) { depth -= (i - numRequiredOptionals) + 1; } else if (!conditionalCast) { @@ -2530,56 +2502,14 @@ namespace { // final M bindings. Expr *result = cast; - // First, handle any required bridging. - if (bridgedThroughClass) { - // If the source type is the bridged class, we don't need the - // actual cast, so grab it's subexpression. - // FIXME: This loses source information. - bool dropCast = srcType->isEqual(bridgedThroughClass); - if (dropCast) - result = cast->getSubExpr(); - - if (requiresConditionalBridging) { - // When conditionally bridging, we need to carry through the - // optional. - if (!dropCast) { - result = new (tc.Context) BindOptionalExpr(result, - cast->getEndLoc(), - failureDepth, - bridgedThroughClass); - result->setImplicit(true); - } - - result = conditionallyBridgeFromObjectiveC(result, destValueType); - if (!result) - return nullptr; - - // Update type sugar. - result->setType(OptionalType::get(destValueType)); - - if (!dropCast) { - result = new (tc.Context) OptionalEvaluationExpr( - result, - OptionalType::get(destValueType)); - } - } else { - result = forceBridgeFromObjectiveC(result, destValueType); - if (!result) - return nullptr; - - // Update type sugar. - result->setType(destValueType); - } - } - if (destOptionals.size() > destExtraOptionals) { if (conditionalCast) { // If the innermost cast fails, the entire expression fails. To // get this behavior, we have to bind and then re-inject the result. // (SILGen should know how to peephole this.) result = new (tc.Context) BindOptionalExpr(result, cast->getEndLoc(), - failureDepth-requiresConditionalBridging, - destValueType); + failureDepth, + destValueType); result->setImplicit(true); } @@ -2663,6 +2593,7 @@ namespace { case CheckedCastKind::ExistentialToConcrete: case CheckedCastKind::ConcreteToArchetype: case CheckedCastKind::ConcreteToUnrelatedExistential: + case CheckedCastKind::BridgeFromObjectiveC: break; } @@ -2743,6 +2674,7 @@ namespace { case CheckedCastKind::ExistentialToConcrete: case CheckedCastKind::ConcreteToArchetype: case CheckedCastKind::ConcreteToUnrelatedExistential: + case CheckedCastKind::BridgeFromObjectiveC: expr->setCastKind(castKind); break; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 5822fcf5637..092b8cf067b 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2289,6 +2289,12 @@ static CheckedCastKind getCheckedCastKind(ConstraintSystem *cs, return CheckedCastKind::DictionaryDowncast; } + // If we can bridge through an Objective-C class, do so. + auto &tc = cs->getTypeChecker(); + if (tc.getDynamicBridgedThroughObjCClass(cs->DC, fromType, toType)) { + return CheckedCastKind::BridgeFromObjectiveC; + } + // We can only downcast to an existential if the destination protocols are // objc and the source type is an objc class or an existential bounded by objc // protocols. @@ -2442,8 +2448,19 @@ ConstraintSystem::simplifyCheckedCastConstraint( case CheckedCastKind::Downcast: case CheckedCastKind::ExistentialToConcrete: { - // Peel off optionals metatypes from the types, because we might cast through - // them. + // Peel off optionals from the types, because we might cast + // through them. + auto toValueType = toType->lookThroughAllAnyOptionalTypes(); + auto fromValueType = fromType->lookThroughAllAnyOptionalTypes(); + + addConstraint(ConstraintKind::Subtype, toValueType, fromValueType, + getConstraintLocator(locator)); + return SolutionKind::Solved; + } + + case CheckedCastKind::BridgeFromObjectiveC: { + // Peel off optionals from the types, because we might cast + // through them. auto toValueType = toType->lookThroughAllAnyOptionalTypes(); auto fromValueType = fromType->lookThroughAllAnyOptionalTypes(); @@ -2458,7 +2475,10 @@ ConstraintSystem::simplifyCheckedCastConstraint( return SolutionKind::Solved; } - addConstraint(ConstraintKind::Subtype, toType, fromType, + Type objCClass = TC.getDynamicBridgedThroughObjCClass(DC, fromValueType, + toValueType); + assert(objCClass && "Type must be bridged"); + addConstraint(ConstraintKind::Subtype, objCClass, fromValueType, getConstraintLocator(locator)); return SolutionKind::Solved; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 6e6ef0825ee..6189cd64eaa 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2198,7 +2198,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType, // If we can bridge through an Objective-C class, do so. if (Type objCClass = getDynamicBridgedThroughObjCClass(dc, fromType, toType)){ if (isSubtypeOf(objCClass, fromType, dc)) - return CheckedCastKind::Downcast; + return CheckedCastKind::BridgeFromObjectiveC; } diagnose(diagLoc, diag::downcast_to_unrelated, origFromType, origToType) diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index de25270e7a7..0af8850acd6 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -957,6 +957,7 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type, case CheckedCastKind::ExistentialToConcrete: case CheckedCastKind::ConcreteToArchetype: case CheckedCastKind::ConcreteToUnrelatedExistential: + case CheckedCastKind::BridgeFromObjectiveC: IP->setCastKind(castKind); break; } diff --git a/test/Constraints/optional.swift b/test/Constraints/optional.swift index 9e838fe911c..74e8e752c15 100644 --- a/test/Constraints/optional.swift +++ b/test/Constraints/optional.swift @@ -62,8 +62,7 @@ func test6(x : T) { class B : A { } func test7(x : A) { - // This should get diagnosed with the "more optional type" error above. - let y = x as? B? // expected-error {{'B?' is not a subtype of 'A'}} + let y = x as? B? // expected-error{{cannot downcast from 'A' to a more optional type 'B?'}} } func test8(x : AnyObject?) { diff --git a/test/SILGen/collection_downcast.swift b/test/SILGen/collection_downcast.swift index ad800c2e628..8bc1431cf50 100644 --- a/test/SILGen/collection_downcast.swift +++ b/test/SILGen/collection_downcast.swift @@ -48,17 +48,14 @@ func testArrayDowncast(array: [AnyObject]) -> [BridgedObjC] { // CHECK-LABEL: sil @_TF19collection_downcast27testArrayDowncastFromObject // CHECK: bb0([[OBJ:%[0-9]+]] : $AnyObject): func testArrayDowncastFromObject(obj: AnyObject) -> [BridgedObjC] { -// CHECK: [[BRIDGE_FN:%[0-9]+]] = function_ref @_TFSs26_forceBridgeFromObjectiveCU__FTPSs9AnyObject_MQ__Q_ -// CHECK: [[NSARRAY_OBJ:%[0-9]+]] = unconditional_checked_cast [[OBJ]] : $AnyObject to $NSArray -// CHECK: apply [[BRIDGE_FN]]<[BridgedObjC]>( + // CHECK: unconditional_checked_cast_addr take_always AnyObject in [[OBJECT_ALLOC:%[0-9]+]]#1 : $*AnyObject to Array in [[VALUE_ALLOC:%[0-9]+]]#1 : $*Array return obj as [BridgedObjC] } // CHECK-LABEL: sil @_TF19collection_downcast28testArrayDowncastFromNSArray // CHECK: bb0([[NSARRAY_OBJ:%[0-9]+]] : $NSArray): func testArrayDowncastFromNSArray(obj: NSArray) -> [BridgedObjC] { -// CHECK: [[BRIDGE_FN:%[0-9]+]] = function_ref @_TFSs26_forceBridgeFromObjectiveCU__FTPSs9AnyObject_MQ__Q_ -// CHECK: apply [[BRIDGE_FN]]<[BridgedObjC]> + // CHECK: unconditional_checked_cast_addr take_always NSArray in [[OBJECT_ALLOC:%[0-9]+]]#1 : $*NSArray to Array in [[VALUE_ALLOC:%[0-9]+]]#1 : $*Array return obj as [BridgedObjC] } @@ -106,9 +103,7 @@ func testArrayIsaBridged(array: [AnyObject]) -> Bool { // CHECK: bb0([[OBJ:%[0-9]+]] : $AnyObject): func testDictionaryDowncastFromObject(obj: AnyObject) -> Dictionary { - // CHECK: [[BRIDGE_FN:%[0-9]+]] = function_ref @_TFSs26_forceBridgeFromObjectiveCU__FTPSs9AnyObject_MQ__Q_ - // CHECK: [[NSDICT_OBJ:%[0-9]+]] = unconditional_checked_cast [[OBJ]] : $AnyObject to $NSDictionary - // CHECK: apply [[BRIDGE_FN]]>( + // CHECK: unconditional_checked_cast_addr take_always AnyObject in [[OBJECT_ALLOC:%[0-9]+]]#1 : $*AnyObject to Dictionary in [[VALUE_ALLOC:%[0-9]+]]#1 : $*Dictionary return obj as Dictionary } diff --git a/test/SILGen/switch_objc.swift b/test/SILGen/switch_objc.swift index 3a6d1a585ed..ff8831bced3 100644 --- a/test/SILGen/switch_objc.swift +++ b/test/SILGen/switch_objc.swift @@ -28,15 +28,3 @@ func matchesEither(#input: Hive, #a: Hive, #b: Hive) -> Bool { return false } } - -// CHECK-LABEL: sil @_TF11switch_objc9bridgedIs -// CHECK: bb0([[OBJ:%[0-9]+]] : $AnyObject): -func bridgedIs(obj: AnyObject) { - // CHECK: function_ref @_TFSs34_conditionallyBridgeFromObjectiveCU__FTPSs9AnyObject_MQ__GSqQ__ - // CHECK: checked_cast_br [[OBJ]] : $AnyObject to $NSString - obj as? String - - // CHECK: [[BRIDGE_FN:%[0-9]+]] = function_ref @_TFSs34_conditionallyBridgeFromObjectiveCU__FTPSs9AnyObject_MQ__GSqQ__ - // CHECK: apply [[BRIDGE_FN]]<[NSString]> - obj as? [NSString] -} diff --git a/test/expr/cast/array_downcast_Foundation.swift b/test/expr/cast/array_downcast_Foundation.swift index 2a14a17596f..5c08b9e309f 100644 --- a/test/expr/cast/array_downcast_Foundation.swift +++ b/test/expr/cast/array_downcast_Foundation.swift @@ -30,8 +30,7 @@ func testDowncastNSArrayToArray(nsarray: NSArray) { func testDowncastOptionalObject(obj: AnyObject?!) -> [String]? { // CHECK: (optional_evaluation_expr implicit type='[String]?' // CHECK-NEXT: (inject_into_optional implicit type='[String]?' - // CHECK-NEXT: (call_expr implicit type='[String]' - // CHECK: (forced_checked_cast_expr type='NSArray'{{.*existential_to_concrete}} + // CHECK: (forced_checked_cast_expr type='[String]'{{.*existential_to_concrete}} // CHECK: (bind_optional_expr implicit type='AnyObject' // CHECK-NEXT: (force_value_expr implicit type='AnyObject?' // CHECK-NEXT: (declref_expr type='AnyObject?!' @@ -45,10 +44,7 @@ func testDowncastOptionalObjectConditional(obj: AnyObject?!) -> [String]?? { // CHECK-NEXT: (optional_evaluation_expr implicit type='[String]?' // CHECK-NEXT: (inject_into_optional implicit type='[String]?' // CHECK-NEXT: (bind_optional_expr implicit type='[String]' - // CHECK-NEXT: (optional_evaluation_expr implicit type='[String]?' - // CHECK-NEXT: (call_expr implicit type='[String]? - // CHECK: (bind_optional_expr implicit type='NSArray' - // CHECK-NEXT: (conditional_checked_cast_expr type='NSArray?' {{.*existential_to_concrete}} writtenType=[String]? + // CHECK-NEXT: (conditional_checked_cast_expr type='[String]?' {{.*existential_to_concrete}} writtenType=[String]? // CHECK-NEXT: (bind_optional_expr implicit type='AnyObject' // CHECK-NEXT: (bind_optional_expr implicit type='AnyObject?' // CHECK-NEXT: (declref_expr type='AnyObject?!' diff --git a/test/expr/cast/as_coerce.swift b/test/expr/cast/as_coerce.swift index 58d2e8298f7..88e5974e01b 100644 --- a/test/expr/cast/as_coerce.swift +++ b/test/expr/cast/as_coerce.swift @@ -64,4 +64,4 @@ class C4 : C3 {} var c: AnyObject = C3() -if let castX = c as C4? {} // expected-error {{cannot convert the expression's type 'AnyObject' to type 'C4?'}} +if let castX = c as C4? {} // expected-error {{cannot downcast from 'AnyObject' to a more optional type 'C4?'}}