Skip to content

Commit 4b6a572

Browse files
committed
Basic Sema support for covariant collection casting.
1 parent 1bb2b7e commit 4b6a572

File tree

7 files changed

+136
-0
lines changed

7 files changed

+136
-0
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ namespace swift {
143143
/// \brief Enable experimental nested generic types feature.
144144
bool EnableExperimentalNestedGenericTypes = false;
145145

146+
/// \brief Enable generalized collection casting.
147+
bool EnableExperimentalCollectionCasts = false;
148+
146149
/// Should we check the target OSs of serialized modules to see that they're
147150
/// new enough?
148151
bool EnableTargetOSChecking = true;

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ def enable_experimental_nested_generic_types :
230230
Flag<["-"], "enable-experimental-nested-generic-types">,
231231
HelpText<"Enable experimental support for nested generic types">;
232232

233+
def enable_experimental_collection_casts :
234+
Flag<["-"], "enable-experimental-collection-casts">,
235+
HelpText<"Enable experimental support for general collection casting">;
236+
233237
def disable_availability_checking : Flag<["-"],
234238
"disable-availability-checking">,
235239
HelpText<"Disable checking for potentially unavailable APIs">;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
743743
Opts.EnableExperimentalNestedGenericTypes |=
744744
Args.hasArg(OPT_enable_experimental_nested_generic_types);
745745

746+
Opts.EnableExperimentalCollectionCasts |=
747+
Args.hasArg(OPT_enable_experimental_collection_casts);
748+
746749
Opts.DisableAvailabilityChecking |=
747750
Args.hasArg(OPT_disable_availability_checking);
748751

lib/Sema/CSSimplify.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3820,6 +3820,15 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
38203820
Type baseType1 = getBaseTypeForArrayType(t1);
38213821
Type baseType2 = getBaseTypeForArrayType(t2);
38223822

3823+
if (TC.getLangOpts().EnableExperimentalCollectionCasts) {
3824+
return matchTypes(baseType1,
3825+
baseType2,
3826+
matchKind,
3827+
subFlags,
3828+
locator.withPathElement(
3829+
ConstraintLocator::PathElement::getGenericArgument(0)));
3830+
}
3831+
38233832
// Look through type variables in the first element type; we need to know
38243833
// it's structure before we can decide whether this can be an array upcast.
38253834
TypeVariableType *baseTypeVar1;
@@ -3873,6 +3882,29 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
38733882
Type key2, value2;
38743883
std::tie(key2, value2) = *isDictionaryType(t2);
38753884

3885+
if (TC.getLangOpts().EnableExperimentalCollectionCasts) {
3886+
// The source key and value types must be subtypes of the destination
3887+
// key and value types, respectively.
3888+
auto result = matchTypes(key1, key2, matchKind, subFlags,
3889+
locator.withPathElement(
3890+
ConstraintLocator::PathElement::getGenericArgument(0)));
3891+
if (result == SolutionKind::Error)
3892+
return result;
3893+
3894+
switch (matchTypes(value1, value2, matchKind, subFlags,
3895+
locator.withPathElement(
3896+
ConstraintLocator::PathElement::getGenericArgument(1)))) {
3897+
case SolutionKind::Solved:
3898+
return result;
3899+
3900+
case SolutionKind::Unsolved:
3901+
return SolutionKind::Unsolved;
3902+
3903+
case SolutionKind::Error:
3904+
return SolutionKind::Error;
3905+
}
3906+
}
3907+
38763908
// Look through type variables in the first key and value type; we
38773909
// need to know their structure before we can decide whether this
38783910
// can be an upcast.
@@ -3979,6 +4011,15 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
39794011
auto t2 = type2->getDesugaredType();
39804012
Type baseType2 = getBaseTypeForSetType(t2);
39814013

4014+
if (TC.getLangOpts().EnableExperimentalCollectionCasts) {
4015+
return matchTypes(baseType1,
4016+
baseType2,
4017+
matchKind,
4018+
subFlags,
4019+
locator.withPathElement(
4020+
ConstraintLocator::PathElement::getGenericArgument(0)));
4021+
}
4022+
39824023
// Look through type variables in the first base type; we need to know
39834024
// their structure before we can decide whether this can be an upcast.
39844025
TypeVariableType *baseTypeVar1 = nullptr;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-swift-frontend -enable-experimental-collection-casts -emit-silgen -sdk %S/Inputs %s | FileCheck %s
2+
3+
struct S { var x, y: Int }
4+
5+
// CHECK-LABEL: sil hidden @_TF27collection_subtype_downcast14array_downcastFT5arrayGSaP___GSqGSaVS_1S__ :
6+
// CHECK: bb0(%0 : $Array<protocol<>>):
7+
// CHECK-NEXT: debug_value %0
8+
// CHECK-NEXT: retain_value %0
9+
// CHECK-NEXT: // function_ref
10+
// CHECK-NEXT: [[FN:%.*]] = function_ref @_TFs21_arrayConditionalCastu0_rFGSax_GSqGSaq___
11+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]<protocol<>, S>(%0) : $@convention(thin) <τ_0_0, τ_0_1> (@owned Array<τ_0_0>) -> @owned Optional<Array<τ_0_1>>
12+
// CHECK-NEXT: release_value %0
13+
// CHECK-NEXT: return [[RESULT]]
14+
func array_downcast(array: [Any]) -> [S]? {
15+
return array as? [S]
16+
}
17+
18+
extension S : Hashable {
19+
var hashValue : Int {
20+
return x + y
21+
}
22+
}
23+
func ==(lhs: S, rhs: S) -> Bool {
24+
return true
25+
}
26+
27+
// FIXME: This entrypoint name should not be bridging-specific
28+
// CHECK-LABEL: sil hidden @_TF27collection_subtype_downcast13dict_downcastFT4dictGVs10DictionaryVS_1SP___GSqGS0_S1_Si__ :
29+
// CHECK: bb0(%0 : $Dictionary<S, protocol<>>):
30+
// CHECK-NEXT: debug_value %0
31+
// CHECK-NEXT: retain_value %0
32+
// CHECK-NEXT: // function_ref
33+
// CHECK-NEXT: [[FN:%.*]] = function_ref @_TFs42_dictionaryBridgeFromObjectiveCConditionalu2_Rxs8Hashable0_S_rFGVs10Dictionaryxq__GSqGS0_q0_q1___
34+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]<S, protocol<>, S, Int>(%0) : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_0 : Hashable, τ_0_2 : Hashable> (@owned Dictionary<τ_0_0, τ_0_1>) -> @owned Optional<Dictionary<τ_0_2, τ_0_3>>
35+
// CHECK-NEXT: release_value %0
36+
// CHECK-NEXT: return [[RESULT]]
37+
func dict_downcast(dict: [S: Any]) -> [S: Int]? {
38+
return dict as? [S: Int]
39+
}
40+
41+
// It's not actually possible to test this for Sets independent of
42+
// the bridging rules.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-swift-frontend -enable-experimental-collection-casts -emit-silgen -sdk %S/Inputs %s | FileCheck %s
2+
3+
struct S { var x, y: Int }
4+
5+
// CHECK-LABEL: sil hidden @_TF25collection_subtype_upcast12array_upcastFT5arrayGSaVS_1S__GSaP__ :
6+
// CHECK: bb0(%0 : $Array<S>):
7+
// CHECK-NEXT: debug_value %0
8+
// CHECK-NEXT: retain_value %0
9+
// CHECK-NEXT: // function_ref
10+
// CHECK-NEXT: [[FN:%.*]] = function_ref @_TFs15_arrayForceCastu0_rFGSax_GSaq__
11+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]<S, protocol<>>(%0) : $@convention(thin) <τ_0_0, τ_0_1> (@owned Array<τ_0_0>) -> @owned Array<τ_0_1>
12+
// CHECK-NEXT: release_value %0
13+
// CHECK-NEXT: return [[RESULT]]
14+
func array_upcast(array: [S]) -> [Any] {
15+
return array
16+
}
17+
18+
extension S : Hashable {
19+
var hashValue : Int {
20+
return x + y
21+
}
22+
}
23+
func ==(lhs: S, rhs: S) -> Bool {
24+
return true
25+
}
26+
27+
// FIXME: This entrypoint name should not be bridging-specific
28+
// CHECK-LABEL: sil hidden @_TF25collection_subtype_upcast11dict_upcastFT4dictGVs10DictionaryVS_1SSi__GS0_S1_P__ :
29+
// CHECK: bb0(%0 : $Dictionary<S, Int>):
30+
// CHECK-NEXT: debug_value %0
31+
// CHECK-NEXT: retain_value %0
32+
// CHECK-NEXT: // function_ref
33+
// CHECK-NEXT: [[FN:%.*]] = function_ref @_TFs29_dictionaryBridgeToObjectiveCu2_Rxs8Hashable0_S_rFGVs10Dictionaryxq__GS0_q0_q1__
34+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]<S, Int, S, protocol<>>(%0) : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_0 : Hashable, τ_0_2 : Hashable> (@owned Dictionary<τ_0_0, τ_0_1>) -> @owned Dictionary<τ_0_2, τ_0_3>
35+
// CHECK-NEXT: release_value %0
36+
// CHECK-NEXT: return [[RESULT]]
37+
func dict_upcast(dict: [S: Int]) -> [S: Any] {
38+
return dict
39+
}
40+
41+
// It's not actually possible to test this for Sets independent of
42+
// the bridging rules.

test/expr/cast/array_bridge.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ var aa: [[A]] = []
4848
var bb: [[B]] = []
4949

5050
aa = bb // expected-error {{cannot assign value of type '[[B]]' to type '[[A]]'}}
51+
bb = aa // expected-error {{cannot assign value of type '[[A]]' to type '[[B]]'}}
5152

5253
class C {
5354
}

0 commit comments

Comments
 (0)