Skip to content

Commit 287fa8e

Browse files
committed
[CS] Refactor IUO handling
The current IUO design always forms a disjunction at the overload reference, for both: - An IUO property `T!`, forming `$T := T? or T` - An IUO-returning function `() -> T!`, forming `$T := () -> T? or () -> T` This is simple in concept, however it's suboptimal for the latter case of IUO-returning functions for a couple of reasons: - The arguments cannot be matched independently of the disjunction - There's some awkwardness when it comes e.g wrapping the overload type in an outer layer of optionality such as `(() -> T!)?`: - The binding logic has to "adjust" the correct reference type after forming the disjunction. - The applicable fn solving logic needs a special case to handle such functions. - The CSApply logic needs various hacks such as ImplicitlyUnwrappedFunctionConversionExpr to make up for the fact that there's no function conversion for IUO functions, we can only force unwrap the function result. - This lead to various crashes in cases where we we'd fail to detect the expr and peephole the force unwrap. - This also lead to crashes where the solver would have a different view of the world than CSApply, as the former would consider an unwrapped IUO function to be of type `() -> T` whereas CSApply would correctly see the overload as being of type `() -> T?`. To remedy these issues, IUO-returning functions no longer have their disjunction formed at the overload reference. Instead, a disjunction is formed when matching result types for the applicable fn constraint, using the callee locator to determine if there's an IUO return to consider. CSApply then consults the callee locator when finishing up applies, and inserts the force unwraps as needed, eliminating ImplicitlyUnwrappedFunctionConversionExpr. This means that now all IUO disjunctions are of the form `$T := T? or T`. This will hopefully allow a further refactoring away from using disjunctions and instead using type variable binding logic to apply the correct unwrapping. Fixes SR-10492.
1 parent f26f136 commit 287fa8e

File tree

9 files changed

+323
-485
lines changed

9 files changed

+323
-485
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4170,15 +4170,6 @@ class ConstraintSystem {
41704170
}
41714171

41724172
private:
4173-
/// Adjust the constraint system to accommodate the given selected overload, and
4174-
/// recompute the type of the referenced declaration.
4175-
///
4176-
/// \returns a pair containing the adjusted opened type of a reference to
4177-
/// this member and a bit indicating whether or not a bind constraint was added.
4178-
std::pair<Type, bool> adjustTypeOfOverloadReference(
4179-
const OverloadChoice &choice, ConstraintLocator *locator, Type boundType,
4180-
Type refType);
4181-
41824173
/// Add the constraints needed to bind an overload's type variable.
41834174
void bindOverloadType(
41844175
const SelectedOverload &overload, Type boundType,
@@ -4490,6 +4481,12 @@ class ConstraintSystem {
44904481
TypeMatchOptions flags, ConstraintLocatorBuilder locator,
44914482
llvm::function_ref<TypeMatchResult()> formUnsolvedResult);
44924483

4484+
/// Matches two function result types for a function application. This is
4485+
/// usually a bind, but also handles e.g IUO unwraps.
4486+
TypeMatchResult matchFunctionResultTypes(Type expectedResult, Type fnResult,
4487+
TypeMatchOptions flags,
4488+
ConstraintLocatorBuilder locator);
4489+
44934490
public: // FIXME: public due to statics in CSSimplify.cpp
44944491
/// Attempt to match up types \c type1 and \c type2, which in effect
44954492
/// is solving the given type constraint between these two types.
@@ -4525,40 +4522,6 @@ class ConstraintSystem {
45254522
}
45264523

45274524
public:
4528-
/// Given a function type where the eventual result type is an optional,
4529-
/// where "eventual result type" is defined as:
4530-
/// 1. The result type is an optional
4531-
/// 2. The result type is a function type with an eventual result
4532-
/// type that is an optional.
4533-
///
4534-
/// return the same function type but with the eventual result type
4535-
/// replaced by its underlying type.
4536-
///
4537-
/// i.e. return (S) -> T for (S) -> T?
4538-
// return (X) -> () -> Y for (X) -> () -> Y?
4539-
Type replaceFinalResultTypeWithUnderlying(AnyFunctionType *fnTy) {
4540-
auto resultTy = fnTy->getResult();
4541-
if (auto *resultFnTy = resultTy->getAs<AnyFunctionType>())
4542-
resultTy = replaceFinalResultTypeWithUnderlying(resultFnTy);
4543-
else {
4544-
auto objType =
4545-
resultTy->getWithoutSpecifierType()->getOptionalObjectType();
4546-
// Preserve l-value through force operation.
4547-
resultTy =
4548-
resultTy->is<LValueType>() ? LValueType::get(objType) : objType;
4549-
}
4550-
4551-
assert(resultTy);
4552-
4553-
if (auto *genericFn = fnTy->getAs<GenericFunctionType>()) {
4554-
return GenericFunctionType::get(genericFn->getGenericSignature(),
4555-
genericFn->getParams(), resultTy,
4556-
genericFn->getExtInfo());
4557-
}
4558-
4559-
return FunctionType::get(fnTy->getParams(), resultTy, fnTy->getExtInfo());
4560-
}
4561-
45624525
// Build a disjunction that attempts both T? and T for a particular
45634526
// type binding. The choice of T? is preferred, and we will not
45644527
// attempt T if we can type check with T?

include/swift/Sema/OverloadChoice.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ enum class OverloadChoiceKind : int {
6060
TupleIndex,
6161
};
6262

63+
/// The kind of implicitly unwrapped optional for an overload reference.
64+
enum class IUOReferenceKind : uint8_t {
65+
/// This overload references an IUO value which may be directly unwrapped.
66+
Value,
67+
68+
/// This overload references a function, the return value of which may be
69+
/// unwrapped.
70+
ReturnValue,
71+
};
72+
6373
/// Describes a particular choice within an overload set.
6474
///
6575
class OverloadChoice {
@@ -258,11 +268,11 @@ class OverloadChoice {
258268
return isDecl() ? getDecl() : nullptr;
259269
}
260270

261-
/// Returns true if this is either a decl for an optional that was
262-
/// declared as one that can be implicitly unwrapped, or is a
263-
/// function-typed decl that has a return value that is implicitly
264-
/// unwrapped.
265-
bool isImplicitlyUnwrappedValueOrReturnValue() const;
271+
/// Retrieve the type of implicitly unwrapped optional for a reference to this
272+
/// overload choice, or \c None if the choice is not for an IUO decl.
273+
Optional<IUOReferenceKind>
274+
getIUOReferenceKind(ConstraintSystem &cs,
275+
bool forSecondApplication = false) const;
266276

267277
bool isKeyPathDynamicMemberLookup() const {
268278
return getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup;

0 commit comments

Comments
 (0)