Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4045,20 +4045,19 @@ namespace {
}

Expr *visitCoerceExpr(CoerceExpr *expr) {
auto *coerced = visitCoerceExprImpl(expr);
if (!coerced)
return nullptr;

// If we need to insert a force-unwrap for coercions of the form
// 'as T!', do so now.
if (hasForcedOptionalResult(expr)) {
auto *coerced = visitCoerceExpr(expr, None);
if (!coerced)
return nullptr;

return forceUnwrapIUO(coerced);
}
if (hasForcedOptionalResult(expr))
coerced = forceUnwrapIUO(coerced);

return visitCoerceExpr(expr, None);
return coerced;
}

Expr *visitCoerceExpr(CoerceExpr *expr, Optional<unsigned> choice) {
Expr *visitCoerceExprImpl(CoerceExpr *expr) {
// Simplify and update the type we're coercing to.
assert(expr->getCastTypeRepr());
const auto toType = simplifyType(cs.getType(expr->getCastTypeRepr()));
Expand Down Expand Up @@ -4107,14 +4106,11 @@ namespace {
// get it from the solution to determine whether we've picked a coercion
// or a bridging conversion.
auto *locator = cs.getConstraintLocator(expr);

if (!choice) {
choice = solution.getDisjunctionChoice(locator);
}
auto choice = solution.getDisjunctionChoice(locator);

// Handle the coercion/bridging of the underlying subexpression, where
// optionality has been removed.
if (*choice == 0) {
if (choice == 0) {
// Convert the subexpression.
Expr *sub = expr->getSubExpr();

Expand All @@ -4128,7 +4124,7 @@ namespace {
}

// Bridging conversion.
assert(*choice == 1 && "should be bridging");
assert(choice == 1 && "should be bridging");

// Handle optional bindings.
Expr *sub = handleOptionalBindings(expr->getSubExpr(), toType,
Expand Down
37 changes: 28 additions & 9 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9555,16 +9555,35 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1,
// we generated somewhat reasonable code that performed a force cast. To
// maintain compatibility with that behavior, allow the coercion between
// two collections, but add a warning fix telling the user to use as! or as?
// instead.
// instead. In Swift 6 mode, this becomes an error.
//
// We only need to perform this compatibility logic if the LHS type is a
// (potentially optional) type variable, as only such a constraint could have
// been previously been left unsolved.
//
// FIXME: Once we get a new language version, change this condition to only
// preserve compatibility for Swift 5.x mode.
auto canUseCompatFix =
rawType1->lookThroughAllOptionalTypes()->isTypeVariableOrMember();
// We only need to perform this compatibility logic if this is a coercion of
// something that isn't a collection expr (as collection exprs would have
// crashed in codegen due to CSApply peepholing them). Additionally, the LHS
// type must be a (potentially optional) type variable, as only such a
// constraint could have been previously been left unsolved.
auto canUseCompatFix = [&]() {
if (Context.isSwiftVersionAtLeast(6))
return false;

if (!rawType1->lookThroughAllOptionalTypes()->isTypeVariableOrMember())
return false;

SmallVector<LocatorPathElt, 4> elts;
auto anchor = locator.getLocatorParts(elts);
if (!elts.empty())
return false;

auto *coercion = getAsExpr<CoerceExpr>(anchor);
if (!coercion)
return false;

auto *subject = coercion->getSubExpr();
while (auto *paren = dyn_cast<ParenExpr>(subject))
subject = paren->getSubExpr();

return !isa<CollectionExpr>(subject);
}();

// Unless we're allowing the collection compatibility fix, the source cannot
// be more optional than the destination.
Expand Down
20 changes: 19 additions & 1 deletion test/Constraints/casts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ func test_coercions_with_overloaded_operator(str: String, optStr: String?, veryO

func id<T>(_ x: T) -> T { x }

func test_compatibility_coercions(_ arr: [Int], _ optArr: [Int]?, _ dict: [String: Int], _ set: Set<Int>) {
func test_compatibility_coercions(_ arr: [Int], _ optArr: [Int]?, _ dict: [String: Int], _ set: Set<Int>, _ i: Int, _ stringAnyDict: [String: Any]) {
// Successful coercions don't raise a warning.
_ = arr as [Any]?
_ = dict as [String: Int]?
Expand Down Expand Up @@ -337,6 +337,24 @@ func test_compatibility_coercions(_ arr: [Int], _ optArr: [Int]?, _ dict: [Strin

// The array can also be inferred to be [Any].
_ = ([] ?? []) as Array // expected-warning {{left side of nil coalescing operator '??' has non-optional type '[Any]', so the right side is never used}}

// rdar://88334481 – Don't apply the compatibility logic for collection literals.
typealias Magic<T> = T
_ = [i] as [String] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
_ = [i] as Magic as [String] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
_ = ([i]) as Magic as [String] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
_ = [i: i] as [String: Any] // expected-error {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}
_ = ([i: i]) as [String: Any] // expected-error {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}
_ = [i: stringAnyDict] as [String: Any] // expected-error {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}

// These are currently not peepholed.
_ = [i].self as Magic as [String] // expected-warning {{coercion from '[Int]' to '[String]' may fail; use 'as?' or 'as!' instead}}
_ = (try [i]) as Magic as [String] // expected-warning {{coercion from '[Int]' to '[String]' may fail; use 'as?' or 'as!' instead}}
// expected-warning@-1 {{no calls to throwing functions occur within 'try' expression}}

// These are wrong, but make sure we don't warn about the value cast always succeeding.
_ = [i: i] as! [String: Any]
_ = [i: stringAnyDict] as! [String: Any]
}

// SR-13088
Expand Down
78 changes: 78 additions & 0 deletions test/Constraints/casts_swift6.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// RUN: %target-typecheck-verify-swift -enable-objc-interop -swift-version 6

// -swift-version 6 is currently asserts-only
// REQUIRES: asserts

func id<T>(_ x: T) -> T { x }
func ohno<T>(_ x: T) -> T? { nil }

// Swift 6 version of the test in casts.swift
func test_compatibility_coercions(_ arr: [Int], _ optArr: [Int]?, _ dict: [String: Int], _ set: Set<Int>, _ i: Int, _ stringAnyDict: [String: Any]) {
// These have always been fine.
_ = arr as [Any]?
_ = dict as [String: Int]?
_ = set as Set<Int>

// These have always been errors.
_ = arr as [String] // expected-error {{cannot convert value of type '[Int]' to type '[String]' in coercion}}
// expected-note@-1 {{arguments to generic parameter 'Element' ('Int' and 'String') are expected to be equal}}
_ = dict as [String: String] // expected-error {{cannot convert value of type '[String : Int]' to type '[String : String]' in coercion}}
// expected-note@-1 {{arguments to generic parameter 'Value' ('Int' and 'String') are expected to be equal}}
_ = dict as [String: String]? // expected-error {{'[String : Int]' is not convertible to '[String : String]?'}}
// expected-note@-1 {{did you mean to use 'as!' to force downcast?}} {{12-14=as!}}
_ = (dict as [String: Int]?) as [String: Int] // expected-error {{value of optional type '[String : Int]?' must be unwrapped to a value of type '[String : Int]'}}
// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
_ = set as Set<String> // expected-error {{cannot convert value of type 'Set<Int>' to type 'Set<String>' in coercion}}
// expected-note@-1 {{arguments to generic parameter 'Element' ('Int' and 'String') are expected to be equal}}

// Make sure we error on the following in Swift 6 mode.

// FIXME: Bad diagnostics (SR-15843)
_ = id(arr) as [String] // expected-error {{type of expression is ambiguous without more context}}
_ = (arr ?? []) as [String] // expected-error {{type of expression is ambiguous without more context}}
_ = (arr ?? [] ?? []) as [String] // expected-error {{type of expression is ambiguous without more context}}
_ = (optArr ?? []) as [String] // expected-error {{type of expression is ambiguous without more context}}

_ = (arr ?? []) as [String]? // expected-error {{'[Int]' is not convertible to '[String]?'}}
// expected-note@-1 {{did you mean to use 'as!' to force downcast?}}
_ = (arr ?? []) as [String?]? // expected-error {{'[Int]' is not convertible to '[String?]?'}}
// expected-note@-1 {{did you mean to use 'as!' to force downcast?}}
_ = (arr ?? []) as [String??]?? // expected-error {{'[Int]' is not convertible to '[String??]??'}}
// expected-note@-1 {{did you mean to use 'as!' to force downcast?}}
_ = (dict ?? [:]) as [String: String?]? // expected-error {{'[String : Int]' is not convertible to '[String : String?]?'}}
// expected-note@-1 {{did you mean to use 'as!' to force downcast?}}
_ = (set ?? []) as Set<String>?? // expected-error {{'Set<Int>' is not convertible to 'Set<String>??'}}
// expected-note@-1 {{did you mean to use 'as!' to force downcast?}}

_ = ohno(ohno(ohno(arr))) as [String] // expected-error {{cannot convert value of type '[Int]???' to type '[String]' in coercion}}
_ = ohno(ohno(ohno(arr))) as [Int] // expected-error {{cannot convert value of type '[Int]???' to type '[Int]' in coercion}}
_ = ohno(ohno(ohno(Set<Int>()))) as Set<String> // expected-error {{cannot convert value of type 'Set<Int>???' to type 'Set<String>' in coercion}}
_ = ohno(ohno(ohno(["": ""]))) as [Int: String] // expected-error {{cannot convert value of type '[String : String]???' to type '[Int : String]' in coercion}}
_ = ohno(ohno(ohno(dict))) as [String: Int] // expected-error {{cannot convert value of type '[String : Int]???' to type '[String : Int]' in coercion}}

// In this case the array literal can be inferred to be [String], so totally
// valid.
_ = ([] ?? []) as [String] // expected-warning {{left side of nil coalescing operator '??' has non-optional type '[String]', so the right side is never used}}
_ = (([] as Optional) ?? []) as [String]

// The array can also be inferred to be [Any].
_ = ([] ?? []) as Array // expected-warning {{left side of nil coalescing operator '??' has non-optional type '[Any]', so the right side is never used}}

// Cases from rdar://88334481
typealias Magic<T> = T
_ = [i] as [String] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
_ = [i] as Magic as [String] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
_ = ([i]) as Magic as [String] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
_ = [i: i] as [String: Any] // expected-error {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}
_ = ([i: i]) as [String: Any] // expected-error {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}
_ = [i: stringAnyDict] as [String: Any] // expected-error {{cannot convert value of type 'Int' to expected dictionary key type 'String'}}

_ = [i].self as Magic as [String] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
_ = (try [i]) as Magic as [String] // expected-error {{cannot convert value of type 'Int' to expected element type 'String'}}
// expected-warning@-1 {{no calls to throwing functions occur within 'try' expression}}

// These are wrong, but make sure we don't warn about the value cast always succeeding.
_ = [i: i] as! [String: Any]
_ = [i: stringAnyDict] as! [String: Any]
}
15 changes: 13 additions & 2 deletions test/SILGen/collection_downcast.swift
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ func testSetDowncastBridgedConditional(_ dict: Set<NSObject>)

func promote<T>(_ x: T) -> T? { nil }

// CHECK-LABEL: sil hidden [ossa] @$s19collection_downcast36testCollectionCompatibilityCoercionsyySaySiG_SayypGSgShySSGSDySSSiGtF
func testCollectionCompatibilityCoercions(_ arr: [Int], _ optArr: [Any]?, _ set: Set<String>, _ dict: [String: Int]) {
// CHECK-LABEL: sil hidden [ossa] @$s19collection_downcast36testCollectionCompatibilityCoercionsyySaySiG_SayypGSgShySSGSDySSSiGSitF
func testCollectionCompatibilityCoercions(_ arr: [Int], _ optArr: [Any]?, _ set: Set<String>, _ dict: [String: Int], _ i: Int) {
// Make sure we generate reasonable code for all of the below examples.

// CHECK: [[CAST_FN:%.+]] = function_ref @$ss15_arrayForceCastySayq_GSayxGr0_lF : $@convention(thin) <τ_0_0, τ_0_1> (@guaranteed Array<τ_0_0>) -> @owned Array<τ_0_1>
Expand Down Expand Up @@ -297,4 +297,15 @@ func testCollectionCompatibilityCoercions(_ arr: [Int], _ optArr: [Any]?, _ set:
// CHECK: [[CAST_FN:%.+]] = function_ref @$ss17_dictionaryUpCastySDyq0_q1_GSDyxq_GSHRzSHR0_r2_lF : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_0 : Hashable, τ_0_2 : Hashable> (@guaranteed Dictionary<τ_0_0, τ_0_1>) -> @owned Dictionary<τ_0_2, τ_0_3>
// CHECK: apply [[CAST_FN]]<String, Int, Int, String>([[DICT]]) : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_0 : Hashable, τ_0_2 : Hashable> (@guaranteed Dictionary<τ_0_0, τ_0_1>) -> @owned Dictionary<τ_0_2, τ_0_3>
_ = promote(promote(promote(dict))) as [Int: String]

typealias Magic<T> = T

// These are currently not peepholed.
// CHECK: [[CAST_FN:%.+]] = function_ref @$ss15_arrayForceCastySayq_GSayxGr0_lF : $@convention(thin) <τ_0_0, τ_0_1> (@guaranteed Array<τ_0_0>) -> @owned Array<τ_0_1>
// CHECK: apply [[CAST_FN]]<Int, String>
[i].self as Magic as [String]

// CHECK: [[CAST_FN:%.+]] = function_ref @$ss15_arrayForceCastySayq_GSayxGr0_lF : $@convention(thin) <τ_0_0, τ_0_1> (@guaranteed Array<τ_0_0>) -> @owned Array<τ_0_1>
// CHECK: apply [[CAST_FN]]<Int, String>
(try [i]) as Magic as [String]
}