Basic Sema support for covariant collection casting.

This commit is contained in:
John McCall
2016-07-13 19:11:48 -07:00
parent 1bb2b7eb4d
commit 4b6a57292f
7 changed files with 136 additions and 0 deletions

View File

@@ -143,6 +143,9 @@ namespace swift {
/// \brief Enable experimental nested generic types feature.
bool EnableExperimentalNestedGenericTypes = false;
/// \brief Enable generalized collection casting.
bool EnableExperimentalCollectionCasts = false;
/// Should we check the target OSs of serialized modules to see that they're
/// new enough?
bool EnableTargetOSChecking = true;

View File

@@ -230,6 +230,10 @@ def enable_experimental_nested_generic_types :
Flag<["-"], "enable-experimental-nested-generic-types">,
HelpText<"Enable experimental support for nested generic types">;
def enable_experimental_collection_casts :
Flag<["-"], "enable-experimental-collection-casts">,
HelpText<"Enable experimental support for general collection casting">;
def disable_availability_checking : Flag<["-"],
"disable-availability-checking">,
HelpText<"Disable checking for potentially unavailable APIs">;

View File

@@ -743,6 +743,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.EnableExperimentalNestedGenericTypes |=
Args.hasArg(OPT_enable_experimental_nested_generic_types);
Opts.EnableExperimentalCollectionCasts |=
Args.hasArg(OPT_enable_experimental_collection_casts);
Opts.DisableAvailabilityChecking |=
Args.hasArg(OPT_disable_availability_checking);

View File

@@ -3820,6 +3820,15 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
Type baseType1 = getBaseTypeForArrayType(t1);
Type baseType2 = getBaseTypeForArrayType(t2);
if (TC.getLangOpts().EnableExperimentalCollectionCasts) {
return matchTypes(baseType1,
baseType2,
matchKind,
subFlags,
locator.withPathElement(
ConstraintLocator::PathElement::getGenericArgument(0)));
}
// Look through type variables in the first element type; we need to know
// it's structure before we can decide whether this can be an array upcast.
TypeVariableType *baseTypeVar1;
@@ -3873,6 +3882,29 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
Type key2, value2;
std::tie(key2, value2) = *isDictionaryType(t2);
if (TC.getLangOpts().EnableExperimentalCollectionCasts) {
// The source key and value types must be subtypes of the destination
// key and value types, respectively.
auto result = matchTypes(key1, key2, matchKind, subFlags,
locator.withPathElement(
ConstraintLocator::PathElement::getGenericArgument(0)));
if (result == SolutionKind::Error)
return result;
switch (matchTypes(value1, value2, matchKind, subFlags,
locator.withPathElement(
ConstraintLocator::PathElement::getGenericArgument(1)))) {
case SolutionKind::Solved:
return result;
case SolutionKind::Unsolved:
return SolutionKind::Unsolved;
case SolutionKind::Error:
return SolutionKind::Error;
}
}
// Look through type variables in the first key and value type; we
// need to know their structure before we can decide whether this
// can be an upcast.
@@ -3979,6 +4011,15 @@ ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restric
auto t2 = type2->getDesugaredType();
Type baseType2 = getBaseTypeForSetType(t2);
if (TC.getLangOpts().EnableExperimentalCollectionCasts) {
return matchTypes(baseType1,
baseType2,
matchKind,
subFlags,
locator.withPathElement(
ConstraintLocator::PathElement::getGenericArgument(0)));
}
// Look through type variables in the first base type; we need to know
// their structure before we can decide whether this can be an upcast.
TypeVariableType *baseTypeVar1 = nullptr;

View File

@@ -0,0 +1,42 @@
// RUN: %target-swift-frontend -enable-experimental-collection-casts -emit-silgen -sdk %S/Inputs %s | FileCheck %s
struct S { var x, y: Int }
// CHECK-LABEL: sil hidden @_TF27collection_subtype_downcast14array_downcastFT5arrayGSaP___GSqGSaVS_1S__ :
// CHECK: bb0(%0 : $Array<protocol<>>):
// CHECK-NEXT: debug_value %0
// CHECK-NEXT: retain_value %0
// CHECK-NEXT: // function_ref
// CHECK-NEXT: [[FN:%.*]] = function_ref @_TFs21_arrayConditionalCastu0_rFGSax_GSqGSaq___
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]<protocol<>, S>(%0) : $@convention(thin) <τ_0_0, τ_0_1> (@owned Array<τ_0_0>) -> @owned Optional<Array<τ_0_1>>
// CHECK-NEXT: release_value %0
// CHECK-NEXT: return [[RESULT]]
func array_downcast(array: [Any]) -> [S]? {
return array as? [S]
}
extension S : Hashable {
var hashValue : Int {
return x + y
}
}
func ==(lhs: S, rhs: S) -> Bool {
return true
}
// FIXME: This entrypoint name should not be bridging-specific
// CHECK-LABEL: sil hidden @_TF27collection_subtype_downcast13dict_downcastFT4dictGVs10DictionaryVS_1SP___GSqGS0_S1_Si__ :
// CHECK: bb0(%0 : $Dictionary<S, protocol<>>):
// CHECK-NEXT: debug_value %0
// CHECK-NEXT: retain_value %0
// CHECK-NEXT: // function_ref
// CHECK-NEXT: [[FN:%.*]] = function_ref @_TFs42_dictionaryBridgeFromObjectiveCConditionalu2_Rxs8Hashable0_S_rFGVs10Dictionaryxq__GSqGS0_q0_q1___
// 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>>
// CHECK-NEXT: release_value %0
// CHECK-NEXT: return [[RESULT]]
func dict_downcast(dict: [S: Any]) -> [S: Int]? {
return dict as? [S: Int]
}
// It's not actually possible to test this for Sets independent of
// the bridging rules.

View File

@@ -0,0 +1,42 @@
// RUN: %target-swift-frontend -enable-experimental-collection-casts -emit-silgen -sdk %S/Inputs %s | FileCheck %s
struct S { var x, y: Int }
// CHECK-LABEL: sil hidden @_TF25collection_subtype_upcast12array_upcastFT5arrayGSaVS_1S__GSaP__ :
// CHECK: bb0(%0 : $Array<S>):
// CHECK-NEXT: debug_value %0
// CHECK-NEXT: retain_value %0
// CHECK-NEXT: // function_ref
// CHECK-NEXT: [[FN:%.*]] = function_ref @_TFs15_arrayForceCastu0_rFGSax_GSaq__
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]<S, protocol<>>(%0) : $@convention(thin) <τ_0_0, τ_0_1> (@owned Array<τ_0_0>) -> @owned Array<τ_0_1>
// CHECK-NEXT: release_value %0
// CHECK-NEXT: return [[RESULT]]
func array_upcast(array: [S]) -> [Any] {
return array
}
extension S : Hashable {
var hashValue : Int {
return x + y
}
}
func ==(lhs: S, rhs: S) -> Bool {
return true
}
// FIXME: This entrypoint name should not be bridging-specific
// CHECK-LABEL: sil hidden @_TF25collection_subtype_upcast11dict_upcastFT4dictGVs10DictionaryVS_1SSi__GS0_S1_P__ :
// CHECK: bb0(%0 : $Dictionary<S, Int>):
// CHECK-NEXT: debug_value %0
// CHECK-NEXT: retain_value %0
// CHECK-NEXT: // function_ref
// CHECK-NEXT: [[FN:%.*]] = function_ref @_TFs29_dictionaryBridgeToObjectiveCu2_Rxs8Hashable0_S_rFGVs10Dictionaryxq__GS0_q0_q1__
// 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>
// CHECK-NEXT: release_value %0
// CHECK-NEXT: return [[RESULT]]
func dict_upcast(dict: [S: Int]) -> [S: Any] {
return dict
}
// It's not actually possible to test this for Sets independent of
// the bridging rules.

View File

@@ -48,6 +48,7 @@ var aa: [[A]] = []
var bb: [[B]] = []
aa = bb // expected-error {{cannot assign value of type '[[B]]' to type '[[A]]'}}
bb = aa // expected-error {{cannot assign value of type '[[A]]' to type '[[B]]'}}
class C {
}