From 8ea2d0dd1070441acece4fe8f5e8435df77f2bd5 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 1 Jun 2017 21:37:12 -0700 Subject: [PATCH] Sema: Fix conditional downcasts from Swift types to CF types Conditional and forced downcasts enter a constraint that almost always succeeds; only when applying the solution do we evaluate the feasability of the cast and determine if it always succeeds, always fails, or conditionally succeeds. This changes how the resulting AST is represented and can also emit diagnostics. If the conditional cast is at this stage determined to always succeed, we treat it as an unconditional cast, going through ExprRewriter::coerceToType() to build the AST for the coercion. However conditional cast constraints don't enter the same restrictions into the solution as unconditional casts do, so coerceToType() would fall over if casting a Swift type to a CF type by first bridging the Swift type to Objective-C. Get around this by checking for this case explicitly when lowering a CoerceExpr. It feels like there's a more fundamental issue here with how casts are modeled in the constraint solver, but I'm not going to try understanding that now. Fixes . --- lib/Sema/CSApply.cpp | 16 ++++++++++++++ .../usr/include/CoreFoundation.h | 4 ++++ test/expr/cast/cf.swift | 22 +++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index acb827f9ccf34..318a03735a50b 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5816,6 +5816,8 @@ Expr *ExprRewriter::buildCollectionUpcastExpr(Expr *expr, Type toType, Expr *ExprRewriter::buildObjCBridgeExpr(Expr *expr, Type toType, ConstraintLocatorBuilder locator) { + auto &tc = cs.getTypeChecker(); + Type fromType = cs.getType(expr); // Bridged collection casts always succeed, so we treat them as @@ -5838,6 +5840,20 @@ Expr *ExprRewriter::buildObjCBridgeExpr(Expr *expr, Type toType, if (!objcExpr) return nullptr; + // We might have a coercion of a Swift type to a CF type toll-free + // bridged to Objective-C. + // + // FIXME: Ideally we would instead have already recorded a restriction + // when solving the constraint, and we wouldn't need to duplicate this + // part of coerceToType() here. + if (auto foreignClass = toType->getClassOrBoundGenericClass()) { + if (foreignClass->getForeignClassKind() == + ClassDecl::ForeignKind::CFType) { + return cs.cacheType( + new (tc.Context) ForeignObjectConversionExpr(objcExpr, toType)); + } + } + return coerceToType(objcExpr, toType, locator); } diff --git a/test/Inputs/clang-importer-sdk/usr/include/CoreFoundation.h b/test/Inputs/clang-importer-sdk/usr/include/CoreFoundation.h index f628500186737..3f96927daf69f 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/CoreFoundation.h +++ b/test/Inputs/clang-importer-sdk/usr/include/CoreFoundation.h @@ -14,6 +14,10 @@ typedef struct __attribute__((objc_bridge(NSString))) __CFString const *CFString typedef struct __CFTree *CFTreeRef; typedef const struct __attribute__((objc_bridge(CFURL))) __CFURL * CFURLRef; +typedef struct __attribute__((objc_bridge(NSDictionary))) __CFDictionary const *CFDictionaryRef; +typedef struct __attribute__((objc_bridge(NSArray))) __CFArray const *CFArrayRef; +typedef struct __attribute__((objc_bridge(NSSet))) __CFSet const *CFSetRef; + typedef CFTypeRef CFAliasForTypeRef; diff --git a/test/expr/cast/cf.swift b/test/expr/cast/cf.swift index 90d1d7fc95314..343d8cf1a1bae 100644 --- a/test/expr/cast/cf.swift +++ b/test/expr/cast/cf.swift @@ -80,3 +80,25 @@ func testCFConvWithIUO(_ x: CFString!, y: NSString!) { acceptNSString(x) acceptCFString(y) } + +func testBridgedCFDowncast(array: [Any], dictionary: [AnyHashable : Any], set: Set) { + let cfArray = array as CFArray + let cfDictionary = dictionary as CFDictionary + let cfSet = set as CFSet + + _ = array as? CFArray // expected-warning {{conditional cast from '[Any]' to 'CFArray' always succeeds}} + _ = dictionary as? CFDictionary // expected-warning {{conditional cast from '[AnyHashable : Any]' to 'CFDictionary' always succeeds}} + _ = set as? CFSet // expected-warning {{conditional cast from 'Set' to 'CFSet' always succeeds}} + + _ = array as! CFArray // expected-warning {{forced cast from '[Any]' to 'CFArray' always succeeds}} + _ = dictionary as! CFDictionary // expected-warning {{forced cast from '[AnyHashable : Any]' to 'CFDictionary' always succeeds}} + _ = set as! CFSet // expected-warning {{forced cast from 'Set' to 'CFSet' always succeeds}} + + _ = cfArray as! [Any] + _ = cfDictionary as! [AnyHashable : Any] + _ = cfSet as! Set + + _ = cfArray as? [Any] + _ = cfDictionary as? [AnyHashable : Any] + _ = cfSet as? Set +}