mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Type checker] Classify the various optional conversion kinds.
1efafbcd9b restricted our classification
of various optional-related conversions (implicit forcing of IUOs,
optional-to-optional conversions, value-to-optional conversions) to
produce a single potential classification. However, in the presence of
type variables, we cannot always determine which particular conversion
restriction might apply. Prior to 1efafb, we produced too many
options; after 1efafb, we produced too few. Here, enumerate a more
bounded (but complete) set of potential conversions, taking into
account embedded type variables.
This is a better fix for rdar://problem/29977523 than simply reverting
1efafb, because it bounds the set of optional conversions we attempt.
This commit is contained in:
@@ -1193,6 +1193,71 @@ static bool isStringCompatiblePointerBaseType(TypeChecker &TC,
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Determine whether this is an implicitly unwrapped optional type.
|
||||
static OptionalTypeKind classifyAsOptionalType(Type type) {
|
||||
if (auto boundGeneric = type->getAs<BoundGenericType>())
|
||||
return boundGeneric->getDecl()->classifyAsOptionalType();
|
||||
|
||||
return OTK_None;
|
||||
}
|
||||
|
||||
/// Determine whether the first type with the given number of optionals
|
||||
/// is potentially more optional than the second type with its number of
|
||||
/// optionals.
|
||||
static bool isPotentiallyMoreOptionalThan(Type objType1,
|
||||
unsigned numOptionals1,
|
||||
Type objType2,
|
||||
unsigned numOptionals2) {
|
||||
if (numOptionals1 <= numOptionals2 && !objType1->isTypeVariableOrMember())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Enumerate all of the applicable optional conversion restrictions
|
||||
static void enumerateOptionalConversionRestrictions(
|
||||
Type type1, Type type2,
|
||||
ConstraintKind kind,
|
||||
llvm::function_ref<void(ConversionRestrictionKind)> fn) {
|
||||
SmallVector<Type, 2> optionals1;
|
||||
Type objType1 = type1->lookThroughAllAnyOptionalTypes(optionals1);
|
||||
|
||||
SmallVector<Type, 2> optionals2;
|
||||
Type objType2 = type2->lookThroughAllAnyOptionalTypes(optionals2);
|
||||
|
||||
if (optionals1.empty() && optionals2.empty())
|
||||
return;
|
||||
|
||||
// Optional-to-optional.
|
||||
if (!optionals1.empty() && !optionals2.empty()) {
|
||||
auto optionalKind1 = classifyAsOptionalType(optionals1.front());
|
||||
auto optionalKind2 = classifyAsOptionalType(optionals2.front());
|
||||
|
||||
// Break cyclic conversions between T? and U! by only allowing it for
|
||||
// conversion constraints.
|
||||
if (kind >= ConstraintKind::Conversion ||
|
||||
!(optionalKind1 == OTK_Optional &&
|
||||
optionalKind2 == OTK_ImplicitlyUnwrappedOptional))
|
||||
fn(ConversionRestrictionKind::OptionalToOptional);
|
||||
}
|
||||
|
||||
// Inject a value into an optional.
|
||||
if (isPotentiallyMoreOptionalThan(objType2, optionals2.size(),
|
||||
objType1, optionals1.size())) {
|
||||
fn(ConversionRestrictionKind::ValueToOptional);
|
||||
}
|
||||
|
||||
// Unwrap an implicitly-unwrapped optional.
|
||||
if (!optionals1.empty() &&
|
||||
classifyAsOptionalType(optionals1.front())
|
||||
== OTK_ImplicitlyUnwrappedOptional &&
|
||||
kind >= ConstraintKind::Conversion &&
|
||||
isPotentiallyMoreOptionalThan(objType1, optionals1.size(),
|
||||
objType2, optionals2.size())) {
|
||||
fn(ConversionRestrictionKind::ForceUnchecked);
|
||||
}
|
||||
}
|
||||
|
||||
ConstraintSystem::SolutionKind
|
||||
ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
|
||||
TypeMatchOptions flags,
|
||||
@@ -1985,57 +2050,17 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
|
||||
}
|
||||
}
|
||||
|
||||
// A value of type T can be converted to type U? if T is convertible to U.
|
||||
// A value of type T? can be converted to type U? if T is convertible to U.
|
||||
// The above conversions also apply to implicitly unwrapped optional types,
|
||||
// except that there is no implicit conversion from T? to T!.
|
||||
{
|
||||
BoundGenericType *boundGenericType2;
|
||||
|
||||
if (concrete && kind >= ConstraintKind::Subtype &&
|
||||
(boundGenericType2 = type2->getAs<BoundGenericType>())) {
|
||||
auto decl2 = boundGenericType2->getDecl();
|
||||
if (auto optionalKind2 = decl2->classifyAsOptionalType()) {
|
||||
assert(boundGenericType2->getGenericArgs().size() == 1);
|
||||
|
||||
BoundGenericType *boundGenericType1 = type1->getAs<BoundGenericType>();
|
||||
if (boundGenericType1) {
|
||||
auto decl1 = boundGenericType1->getDecl();
|
||||
if (decl1 == decl2) {
|
||||
assert(boundGenericType1->getGenericArgs().size() == 1);
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::OptionalToOptional);
|
||||
} else if (optionalKind2 == OTK_Optional &&
|
||||
decl1 == TC.Context.getImplicitlyUnwrappedOptionalDecl()) {
|
||||
assert(boundGenericType1->getGenericArgs().size() == 1);
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::OptionalToOptional);
|
||||
} else if (optionalKind2 == OTK_ImplicitlyUnwrappedOptional &&
|
||||
kind >= ConstraintKind::Conversion &&
|
||||
decl1 == TC.Context.getOptionalDecl()) {
|
||||
assert(boundGenericType1->getGenericArgs().size() == 1);
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::OptionalToOptional);
|
||||
}
|
||||
}
|
||||
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::ValueToOptional);
|
||||
}
|
||||
}
|
||||
// A value of type T! can be converted to type U if T is convertible
|
||||
// to U by force-unwrapping the source value.
|
||||
// A value of type T, T?, or T! can be converted to type U? or U! if
|
||||
// T is convertible to U.
|
||||
if (concrete && kind >= ConstraintKind::Subtype) {
|
||||
enumerateOptionalConversionRestrictions(type1, type2, kind,
|
||||
[&](ConversionRestrictionKind restriction) {
|
||||
conversionsOrFixes.push_back(restriction);
|
||||
});
|
||||
}
|
||||
|
||||
// A value of type T! can be (unsafely) forced to U if T
|
||||
// is convertible to U.
|
||||
{
|
||||
Type objectType1;
|
||||
if (concrete && kind >= ConstraintKind::Conversion &&
|
||||
(objectType1 = lookThroughImplicitlyUnwrappedOptionalType(type1))) {
|
||||
conversionsOrFixes.push_back(
|
||||
ConversionRestrictionKind::ForceUnchecked);
|
||||
}
|
||||
}
|
||||
|
||||
// Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
|
||||
// literals.
|
||||
if (auto elt = locator.last()) {
|
||||
@@ -3743,7 +3768,21 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
increaseScore(SK_FunctionConversion);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Local function to form an unsolved result.
|
||||
auto formUnsolved = [&] {
|
||||
if (flags.contains(TMF_GenerateConstraints)) {
|
||||
addUnsolvedConstraint(
|
||||
Constraint::createRestricted(
|
||||
*this, matchKind, restriction, type1, type2,
|
||||
getConstraintLocator(locator)));
|
||||
|
||||
return SolutionKind::Solved;
|
||||
}
|
||||
|
||||
return SolutionKind::Unsolved;
|
||||
};
|
||||
|
||||
// We'll apply user conversions for operator arguments at the application
|
||||
// site.
|
||||
if (matchKind == ConstraintKind::OperatorArgumentConversion) {
|
||||
@@ -3806,10 +3845,18 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
increaseScore(SK_ValueToOptional);
|
||||
|
||||
assert(matchKind >= ConstraintKind::Subtype);
|
||||
auto generic2 = type2->castTo<BoundGenericType>();
|
||||
assert(generic2->getDecl()->classifyAsOptionalType());
|
||||
return matchTypes(type1, generic2->getGenericArgs()[0],
|
||||
matchKind, (subflags | TMF_UnwrappingOptional), locator);
|
||||
if (type2->isTypeVariableOrMember())
|
||||
return formUnsolved();
|
||||
|
||||
if (auto generic2 = type2->getAs<BoundGenericType>()) {
|
||||
if (generic2->getDecl()->classifyAsOptionalType()) {
|
||||
return matchTypes(type1, generic2->getGenericArgs()[0],
|
||||
matchKind, (subflags | TMF_UnwrappingOptional),
|
||||
locator);
|
||||
}
|
||||
}
|
||||
|
||||
return SolutionKind::Error;
|
||||
}
|
||||
|
||||
// for $< in { <, <c, <oc }:
|
||||
@@ -3820,16 +3867,24 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
// T <c U ===> T? <c U!
|
||||
case ConversionRestrictionKind::OptionalToOptional: {
|
||||
addContextualScore();
|
||||
|
||||
if (type1->isTypeVariableOrMember() || type2->isTypeVariableOrMember())
|
||||
return formUnsolved();
|
||||
|
||||
assert(matchKind >= ConstraintKind::Subtype);
|
||||
auto generic1 = type1->castTo<BoundGenericType>();
|
||||
auto generic2 = type2->castTo<BoundGenericType>();
|
||||
assert(generic1->getDecl()->classifyAsOptionalType());
|
||||
assert(generic2->getDecl()->classifyAsOptionalType());
|
||||
return matchTypes(generic1->getGenericArgs()[0],
|
||||
generic2->getGenericArgs()[0],
|
||||
matchKind, subflags,
|
||||
locator.withPathElement(
|
||||
LocatorPathElt::getGenericArgument(0)));
|
||||
if (auto generic1 = type1->getAs<BoundGenericType>()) {
|
||||
if (auto generic2 = type2->getAs<BoundGenericType>()) {
|
||||
if (generic1->getDecl()->classifyAsOptionalType() &&
|
||||
generic2->getDecl()->classifyAsOptionalType())
|
||||
return matchTypes(generic1->getGenericArgs()[0],
|
||||
generic2->getGenericArgs()[0],
|
||||
matchKind, subflags,
|
||||
locator.withPathElement(
|
||||
LocatorPathElt::getGenericArgument(0)));
|
||||
}
|
||||
}
|
||||
|
||||
return SolutionKind::Error;
|
||||
}
|
||||
|
||||
// T <c U ===> T! <c U
|
||||
@@ -3843,16 +3898,24 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
case ConversionRestrictionKind::ForceUnchecked: {
|
||||
addContextualScore();
|
||||
assert(matchKind >= ConstraintKind::Conversion);
|
||||
auto boundGenericType1 = type1->castTo<BoundGenericType>();
|
||||
assert(boundGenericType1->getDecl()->classifyAsOptionalType()
|
||||
== OTK_ImplicitlyUnwrappedOptional);
|
||||
assert(boundGenericType1->getGenericArgs().size() == 1);
|
||||
Type valueType1 = boundGenericType1->getGenericArgs()[0];
|
||||
increaseScore(SK_ForceUnchecked);
|
||||
return matchTypes(valueType1, type2,
|
||||
matchKind, subflags,
|
||||
locator.withPathElement(
|
||||
LocatorPathElt::getGenericArgument(0)));
|
||||
|
||||
if (type1->isTypeVariableOrMember())
|
||||
return formUnsolved();
|
||||
|
||||
if (auto boundGenericType1 = type1->getAs<BoundGenericType>()) {
|
||||
if (boundGenericType1->getDecl()->classifyAsOptionalType()
|
||||
== OTK_ImplicitlyUnwrappedOptional) {
|
||||
assert(boundGenericType1->getGenericArgs().size() == 1);
|
||||
Type valueType1 = boundGenericType1->getGenericArgs()[0];
|
||||
increaseScore(SK_ForceUnchecked);
|
||||
return matchTypes(valueType1, type2,
|
||||
matchKind, subflags,
|
||||
locator.withPathElement(
|
||||
LocatorPathElt::getGenericArgument(0)));
|
||||
}
|
||||
}
|
||||
|
||||
return SolutionKind::Error;
|
||||
}
|
||||
|
||||
case ConversionRestrictionKind::ClassMetatypeToAnyObject:
|
||||
|
||||
Reference in New Issue
Block a user