Skip to content

Sema: Allow collection downcast in cast pattern and remove the diagnostic that it is not implemented #60682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 2, 2022
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
4 changes: 0 additions & 4 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4467,10 +4467,6 @@ NOTE(unowned_assignment_requires_strong,none,
"a strong reference is required to prevent the instance from being "
"deallocated", ())

ERROR(isa_collection_downcast_pattern_value_unimplemented,none,
"collection downcast in cast pattern is not implemented; use an explicit "
"downcast to %0 instead", (Type))

//------------------------------------------------------------------------------
// MARK: Error-handling diagnostics
//------------------------------------------------------------------------------
Expand Down
11 changes: 1 addition & 10 deletions lib/Sema/TypeCheckPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1385,16 +1385,7 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern,
// Valid checks.
case CheckedCastKind::ArrayDowncast:
case CheckedCastKind::DictionaryDowncast:
case CheckedCastKind::SetDowncast: {
diags.diagnose(IP->getLoc(),
diag::isa_collection_downcast_pattern_value_unimplemented,
IP->getCastType());
IP->setType(ErrorType::get(Context));
if (Pattern *sub = IP->getSubPattern())
sub->forEachVariable([](VarDecl *VD) { VD->setInvalid(); });
return P;
}

case CheckedCastKind::SetDowncast:
case CheckedCastKind::ValueCast:
IP->setCastKind(castKind);
break;
Expand Down
59 changes: 47 additions & 12 deletions test/Parse/matching_patterns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -289,22 +289,57 @@ case (let (_, _, _)) + 1:
()
}

// FIXME: We don't currently allow subpatterns for "isa" patterns that
// require interesting conditional downcasts.
class Base { }
class Derived : Base { }
// "isa" patterns.

// https://github.com/apple/swift/issues/56139
// Allow subpatterns for "isa" patterns that require conditional
// collection downcasts
do {
class Base { }
class Derived : Base { }

switch [Derived(), Derived(), Base()] {
case let ds as [Derived]: // expected-error{{collection downcast in cast pattern is not implemented; use an explicit downcast to '[Derived]' instead}}
()
case is [Derived]: // expected-error{{collection downcast in cast pattern is not implemented; use an explicit downcast to '[Derived]' instead}}
()
let arr: [Base]

default:
()
}
if case let _ as [Derived] = arr {}
// expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}}
guard case let _ as [Derived] = arr else {}
// expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}}
while case let _ as [Derived] = arr {}
// expected-warning@-1 {{'let' pattern has no effect; sub-pattern didn't bind any variables}}

// FIXME: https://github.com/apple/swift/issues/61850
// expected-warning@+1 {{heterogeneous collection literal could only be inferred to '[[Base]]'; add explicit type annotation if this is intentional}}
for case _ as [Derived] in [arr] {}

if case is [Derived] = arr {}

guard case is [Derived] = arr else {}

while case is [Derived] = arr {}

for case is [Derived] in [arr] {}

switch arr {
case let ds as [Derived]:
// expected-warning@-1 {{immutable value 'ds' was never used; consider replacing with '_' or removing it}}
()
case is [Derived]:
()

default:
()
}

let _ = { (arr: [Base]) -> Void in
switch arr {
case let ds as [Derived]:
// expected-warning@-1 {{immutable value 'ds' was never used; consider replacing with '_' or removing it}}
()
default:
()
}
}
}

// Optional patterns.
let op1 : Int?
Expand Down
97 changes: 97 additions & 0 deletions test/SILGen/statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,103 @@ func test_as_pattern(_ y : BaseClass) -> DerivedClass {
// CHECK-NEXT: return [[RESULT]] : $DerivedClass
return result
}

// https://github.com/apple/swift/issues/56139

// CHECK-LABEL: sil hidden [ossa] @$s10statements31test_isa_pattern_array_downcastyySayAA9BaseClassCGF : $@convention(thin) (@guaranteed Array<BaseClass>) -> () {
func test_isa_pattern_array_downcast(_ arr: [BaseClass]) {
// CHECK: [[ARR_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF:ss21_arrayConditionalCastySayq_GSgSayxGr0_lF]] : $@convention(thin) <τ_0_0, τ_0_1> (@guaranteed Array<τ_0_0>) -> @owned Optional<Array<τ_0_1>>
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[ARR_CAST_FN]]<BaseClass, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
if case _ as [DerivedClass] = arr {}
// CHECK: [[ARR_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[ARR_CAST_FN]]<BaseClass, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
guard case _ as [DerivedClass] = arr else {}
// CHECK: [[ARR_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[ARR_CAST_FN]]<BaseClass, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
while case _ as [DerivedClass] = arr {}

// CHECK: [[ARR_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[ARR_CAST_FN]]<BaseClass, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
if case is [DerivedClass] = arr {}
// CHECK: [[ARR_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[ARR_CAST_FN]]<BaseClass, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
guard case is [DerivedClass] = arr else {}
// CHECK: [[ARR_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[ARR_CAST_FN]]<BaseClass, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
while case is [DerivedClass] = arr {}
}
// CHECK: } // end sil function '$s10statements31test_isa_pattern_array_downcastyySayAA9BaseClassCGF'

// CHECK-LABEL: sil hidden [ossa] @$s10statements36test_isa_pattern_dictionary_downcastyySDySSAA9BaseClassCGF : $@convention(thin) (@guaranteed Dictionary<String, BaseClass>) -> () {
func test_isa_pattern_dictionary_downcast(_ dict: Dictionary<String, BaseClass>) {
// CHECK: [[DICT_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF:ss30_dictionaryDownCastConditionalySDyq0_q1_GSgSDyxq_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 Optional<Dictionary<τ_0_2, τ_0_3>>
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[DICT_CAST_FN]]<String, BaseClass, String, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
if case _ as [String : DerivedClass] = dict {}
// CHECK: [[DICT_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[DICT_CAST_FN]]<String, BaseClass, String, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
guard case _ as [String : DerivedClass] = dict else {}
// CHECK: [[DICT_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[DICT_CAST_FN]]<String, BaseClass, String, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
while case _ as [String : DerivedClass] = dict {}

// CHECK: [[DICT_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[DICT_CAST_FN]]<String, BaseClass, String, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
if case is [String : DerivedClass] = dict {}
// CHECK: [[DICT_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[DICT_CAST_FN]]<String, BaseClass, String, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
guard case is [String : DerivedClass] = dict else {}
// CHECK: [[DICT_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[DICT_CAST_FN]]<String, BaseClass, String, DerivedClass>
// CHECK-NEXT: switch_enum [[RESULT]]
while case is [String : DerivedClass] = dict {}
}
// CHECK: } // end sil function '$s10statements36test_isa_pattern_dictionary_downcastyySDySSAA9BaseClassCGF'

// CHECK-LABEL: sil hidden [ossa] @$s10statements29test_isa_pattern_set_downcastyyShyxGSHRzlF : $@convention(thin) <T where T : Hashable> (@guaranteed Set<T>) -> () {
func test_isa_pattern_set_downcast<T: Hashable>(_ set: Set<T>) {
// CHECK: [[SET_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF:ss23_setDownCastConditionalyShyq_GSgShyxGSHRzSHR_r0_lF]] : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 : Hashable, τ_0_1 : Hashable> (@guaranteed Set<τ_0_0>) -> @owned Optional<Set<τ_0_1>>
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[SET_CAST_FN]]<T, Bool>
// CHECK-NEXT: switch_enum [[RESULT]]
if case let t as Set<Bool> = set {}
// FIXME: Get rid of these warnings when https://github.com/apple/swift/issues/60808 is fixed
// expected-warning@-2 {{immutable value 't' was never used; consider replacing with '_' or removing it}}
// CHECK: [[SET_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[SET_CAST_FN]]<T, Bool>
// CHECK-NEXT: switch_enum [[RESULT]]
guard case let t as Set<Bool> = set else {}
// expected-warning@-1 {{immutable value 't' was never used; consider replacing with '_' or removing it}}
// CHECK: [[SET_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[SET_CAST_FN]]<T, Bool>
// CHECK-NEXT: switch_enum [[RESULT]]
while case let t as Set<Bool> = set {}
// expected-warning@-1 {{immutable value 't' was never used; consider replacing with '_' or removing it}}

// CHECK: [[SET_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[SET_CAST_FN]]<T, Int>
// CHECK-NEXT: switch_enum [[RESULT]]
if case is Set<Int> = set {}
// CHECK: [[SET_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[SET_CAST_FN]]<T, Int>
// CHECK-NEXT: switch_enum [[RESULT]]
guard case is Set<Int> = set else {}
// CHECK: [[SET_CAST_FN:%[0-9]+]] = function_ref @$[[FN_REF]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[SET_CAST_FN]]<T, Int>
// CHECK-NEXT: switch_enum [[RESULT]]
while case is Set<Int> = set {}
}
// CHECK: } // end sil function '$s10statements29test_isa_pattern_set_downcastyyShyxGSHRzlF'

// CHECK-LABEL: sil hidden [ossa] @$s10statements22let_else_tuple_bindingyS2i_SitSgF
func let_else_tuple_binding(_ a : (Int, Int)?) -> Int {

Expand Down
127 changes: 127 additions & 0 deletions test/SILGen/switch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,133 @@ func test_isa_class_2(x: B) -> AnyObject {
}
// CHECK: } // end sil function '$s6switch16test_isa_class_21xyXlAA1BC_tF'

// https://github.com/apple/swift/issues/56139

// CHECK-LABEL: sil hidden [ossa] @$s6switch31test_isa_pattern_array_downcast2psySayAA1P_pG_tF : $@convention(thin) (@guaranteed Array<any P>) -> () {
func test_isa_pattern_array_downcast(ps: Array<P>) {
// CHECK: bb0(%0 : @guaranteed $Array<any P>):
switch ps {
// CHECK: checked_cast_addr_br copy_on_success Array<any P> in [[P:%[0-9]+]] : $*Array<any P> to Array<X> in {{%[0-9]+}} : $*Array<X>, [[IS_X:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
case is [X]:
// CHECK: [[IS_X]]:
// CHECK: function_ref @$s6switch1ayyF
a()
// CHECK: br [[CONT:bb[0-9]+]]

// CHECK: [[IS_NOT_X]]:
// CHECK: [[DEST:%[0-9]+]] = alloc_stack $Array<Y>
// CHECK-NEXT: checked_cast_addr_br copy_on_success Array<any P> in [[P:%[0-9]+]] : $*Array<any P> to Array<Y> in [[DEST]] : $*Array<Y>, [[IS_Y:bb[0-9]+]], [[IS_NOT_Y:bb[0-9]+]]
case let _ as [Y]:
// CHECK: [[IS_Y]]:
// CHECK-NEXT: load [take] [[DEST]] : $*Array<Y>
// CHECK: function_ref @$s6switch1byyF
b()
// CHECK: br [[CONT]]
default:
// CHECK: [[IS_NOT_Y]]:
// CHECK: function_ref @$s6switch1cyyF
c()
// CHECK: br [[CONT]]
}
// CHECK: [[CONT]]:
// CHECK: function_ref @$s6switch1dyyF
d()
}
// CHECK: } // end sil function '$s6switch31test_isa_pattern_array_downcast2psySayAA1P_pG_tF'

// CHECK-LABEL: sil hidden [ossa] @$s6switch39test_isa_pattern_array_downcast_closureyyF : $@convention(thin) () -> () {
// CHECK: function_ref @$s6switch39test_isa_pattern_array_downcast_closureyyFySayAA1P_pGcfU_
// CHECK: } // end sil function '$s6switch39test_isa_pattern_array_downcast_closureyyF'
func test_isa_pattern_array_downcast_closure() {
// CHECK-LABEL: sil private [ossa] @$s6switch39test_isa_pattern_array_downcast_closureyyFySayAA1P_pGcfU_ : $@convention(thin) (@guaranteed Array<any P>) -> () {
let _ = { (ps: [P]) -> Void in
// CHECK: bb0(%0 : @guaranteed $Array<any P>):
switch ps {
// CHECK: [[DEST:%[0-9]+]] = alloc_stack $Array<X>
// CHECK-NEXT: checked_cast_addr_br copy_on_success Array<any P> in [[P:%[0-9]+]] : $*Array<any P> to Array<X> in [[DEST]] : $*Array<X>, [[IS_X:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
case let _ as [X]:
// CHECK: [[IS_X]]:
// CHECK-NEXT: load [take] [[DEST]] : $*Array<X>
// CHECK: function_ref @$s6switch1ayyF
a()
// CHECK: br [[CONT:bb[0-9]+]]

// CHECK: [[IS_NOT_X]]:
// CHECK: br [[DEF:bb[0-9]+]]
default:
// CHECK: [[DEF]]:
// CHECK: function_ref @$s6switch1byyF
b()
// CHECK: br [[CONT]]
}
}
// CHECK: } // end sil function '$s6switch39test_isa_pattern_array_downcast_closureyyFySayAA1P_pGcfU_'
}

// CHECK-LABEL: sil hidden [ossa] @$s6switch30test_isa_pattern_dict_downcast2psySDySSAA1P_pG_tF : $@convention(thin) (@guaranteed Dictionary<String, any P>) -> () {
func test_isa_pattern_dict_downcast(ps: Dictionary<String, P>) {
// CHECK: bb0(%0 : @guaranteed $Dictionary<String, any P>):
switch ps {
// CHECK: checked_cast_addr_br copy_on_success Dictionary<String, any P> in [[P:%[0-9]+]] : $*Dictionary<String, any P> to Dictionary<String, X> in {{%[0-9]+}} : $*Dictionary<String, X>, [[IS_X:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
case is [String : X]:
// CHECK: [[IS_X]]:
// CHECK: function_ref @$s6switch1ayyF
a()
// CHECK: br [[CONT:bb[0-9]+]]

// CHECK: [[IS_NOT_X]]:
// CHECK: [[DEST:%[0-9]+]] = alloc_stack $Dictionary<String, Y>
// CHECK-NEXT: checked_cast_addr_br copy_on_success Dictionary<String, any P> in [[P:%[0-9]+]] : $*Dictionary<String, any P> to Dictionary<String, Y> in [[DEST]] : $*Dictionary<String, Y>, [[IS_Y:bb[0-9]+]], [[IS_NOT_Y:bb[0-9]+]]
case let _ as [String : Y]:
// CHECK: [[IS_Y]]:
// CHECK-NEXT: load [take] [[DEST]] : $*Dictionary<String, Y>
// CHECK: function_ref @$s6switch1byyF
b()
// CHECK: br [[CONT]]
default:
// CHECK: [[IS_NOT_Y]]:
// CHECK: function_ref @$s6switch1cyyF
c()
// CHECK: br [[CONT]]
}
// CHECK: [[CONT]]:
// CHECK: function_ref @$s6switch1dyyF
d()
}
// CHECK-LABEL: } // end sil function '$s6switch30test_isa_pattern_dict_downcast2psySDySSAA1P_pG_tF'

// CHECK-LABEL: sil hidden [ossa] @$s6switch29test_isa_pattern_set_downcast2psyShyxG_tSHRzlF : $@convention(thin) <T where T : Hashable> (@guaranteed Set<T>) -> () {
func test_isa_pattern_set_downcast<T: Hashable>(ps: Set<T>) {
// CHECK: bb0(%0 : @guaranteed $Set<T>):
switch ps {
// CHECK: checked_cast_addr_br copy_on_success Set<T> in [[P:%[0-9]+]] : $*Set<T> to Set<Int> in {{%[0-9]+}} : $*Set<Int>, [[IS_INT:bb[0-9]+]], [[IS_NOT_INT:bb[0-9]+]]
case is Set<Int>:
// CHECK: [[IS_INT]]:
// CHECK: function_ref @$s6switch1ayyF
a()
// CHECK: br [[CONT:bb[0-9]+]]

// CHECK: [[IS_NOT_INT]]:
// CHECK: [[DEST:%[0-9]+]] = alloc_stack $Set<Bool>
// CHECK-NEXT: checked_cast_addr_br copy_on_success Set<T> in [[P:%[0-9]+]] : $*Set<T> to Set<Bool> in [[DEST]] : $*Set<Bool>, [[IS_BOOL:bb[0-9]+]], [[IS_NOT_BOOL:bb[0-9]+]]
case let _ as Set<Bool>:
// CHECK: [[IS_BOOL]]:
// CHECK-NEXT: load [take] [[DEST]] : $*Set<Bool>
// CHECK: function_ref @$s6switch1byyF
b()
// CHECK: br [[CONT]]
default:
// CHECK: [[IS_NOT_BOOL]]:
// CHECK: function_ref @$s6switch1cyyF
c()
// CHECK: br [[CONT]]
}
// CHECK: [[CONT]]:
// CHECK: function_ref @$s6switch1dyyF
d()
}
// CHECK: } // end sil function '$s6switch29test_isa_pattern_set_downcast2psyShyxG_tSHRzlF'

enum MaybePair {
case Neither
case Left(Int)
Expand Down