mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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 <rdar://problem/32227571>.
105 lines
4.2 KiB
Swift
105 lines
4.2 KiB
Swift
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %s -verify
|
|
|
|
// REQUIRES: objc_interop
|
|
|
|
import CoreFoundation
|
|
import Foundation
|
|
|
|
func testCFToObjC(_ cfStr: CFString, cfMutableStr: CFMutableString) {
|
|
var nsStr: NSString = cfStr
|
|
nsStr = cfMutableStr
|
|
_ = nsStr
|
|
|
|
var nsMutableStr: NSMutableString = cfMutableStr
|
|
nsMutableStr = cfStr // expected-error{{cannot assign value of type 'CFString' to type 'NSMutableString'}}
|
|
|
|
// sanity check
|
|
nsStr = nsMutableStr
|
|
}
|
|
|
|
func testObjCToCF(_ nsStr: NSString, nsMutableStr: NSMutableString) {
|
|
var cfStr: CFString = nsStr
|
|
cfStr = nsMutableStr
|
|
|
|
var cfMutableStr: CFMutableString = nsMutableStr
|
|
cfMutableStr = cfStr // expected-error{{cannot assign value of type 'CFString' to type 'CFMutableString'}}
|
|
|
|
// sanity check
|
|
cfStr = cfMutableStr
|
|
}
|
|
|
|
func testCFToNative(_ cfStr: CFString, cfMutableStr: CFMutableString) {
|
|
var str = cfStr as String
|
|
str = cfMutableStr as String
|
|
_ = str
|
|
}
|
|
|
|
func testNativeToCF(_ str: String) {
|
|
var cfStr = str as CFString
|
|
var cfMutableStr = str as CFMutableString // expected-error{{'String' is not convertible to 'CFMutableString'}} {{26-28=as!}}
|
|
}
|
|
|
|
func testCFToAnyObject(_ cfStr: CFString, cfMutableStr: CFMutableString,
|
|
cfTree: CFTree) {
|
|
var anyObject: AnyObject = cfStr
|
|
anyObject = cfMutableStr
|
|
anyObject = cfTree
|
|
_ = anyObject
|
|
}
|
|
|
|
func testAnyObjectToCF(_ anyObject: AnyObject) {
|
|
var cfStr: CFString = anyObject as! CFString
|
|
var _: CFMutableString = anyObject as! CFMutableString
|
|
var _: CFTree = anyObject as! CFTree
|
|
|
|
// No implicit conversions.
|
|
cfStr = anyObject // expected-error{{'AnyObject' is not convertible to 'CFString'; did you mean to use 'as!' to force downcast?}} {{20-20= as! CFString}}
|
|
_ = cfStr
|
|
}
|
|
|
|
func testUncheckableCasts(_ anyObject: AnyObject, nsObject: NSObject,
|
|
anyObjectType: AnyObject.Type,
|
|
nsObjectType: NSObject.Type) {
|
|
if let _ = anyObject as? CFString { } // expected-error{{conditional downcast to CoreFoundation type 'CFString' will always succeed}}
|
|
if let _ = nsObject as? CFString { } // expected-error{{conditional downcast to CoreFoundation type 'CFString' will always succeed}}
|
|
|
|
if let _ = anyObject as? CFTree { } // expected-error{{conditional downcast to CoreFoundation type 'CFTree' will always succeed}}
|
|
if let _ = nsObject as? CFTree { } // expected-error{{will always succeed}}
|
|
|
|
if let _ = anyObjectType as? CFString.Type { } // expected-error{{conditional downcast to CoreFoundation type 'CFString.Type' will always succeed}}
|
|
if let _ = nsObjectType as? CFString.Type { } // expected-error{{conditional downcast to CoreFoundation type 'CFString.Type' will always succeed}}
|
|
|
|
if let _ = anyObjectType as? CFTree.Type { } // expected-error{{conditional downcast to CoreFoundation type 'CFTree.Type' will always succeed}}
|
|
if let _ = nsObjectType as? CFTree.Type { } // expected-error{{will always succeed}}
|
|
}
|
|
|
|
func testCFConvWithIUO(_ x: CFString!, y: NSString!) {
|
|
func acceptCFString(_ a: CFString!) { }
|
|
func acceptNSString(_ b: NSString!) { }
|
|
|
|
acceptNSString(x)
|
|
acceptCFString(y)
|
|
}
|
|
|
|
func testBridgedCFDowncast(array: [Any], dictionary: [AnyHashable : Any], set: Set<AnyHashable>) {
|
|
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<AnyHashable>' 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<AnyHashable>' to 'CFSet' always succeeds}}
|
|
|
|
_ = cfArray as! [Any]
|
|
_ = cfDictionary as! [AnyHashable : Any]
|
|
_ = cfSet as! Set<AnyHashable>
|
|
|
|
_ = cfArray as? [Any]
|
|
_ = cfDictionary as? [AnyHashable : Any]
|
|
_ = cfSet as? Set<AnyHashable>
|
|
}
|