mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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.
This commit is contained in:
@@ -4170,15 +4170,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/// Adjust the constraint system to accommodate the given selected overload, and
|
||||
/// recompute the type of the referenced declaration.
|
||||
///
|
||||
/// \returns a pair containing the adjusted opened type of a reference to
|
||||
/// this member and a bit indicating whether or not a bind constraint was added.
|
||||
std::pair<Type, bool> adjustTypeOfOverloadReference(
|
||||
const OverloadChoice &choice, ConstraintLocator *locator, Type boundType,
|
||||
Type refType);
|
||||
|
||||
/// Add the constraints needed to bind an overload's type variable.
|
||||
void bindOverloadType(
|
||||
const SelectedOverload &overload, Type boundType,
|
||||
@@ -4490,6 +4481,12 @@ public:
|
||||
TypeMatchOptions flags, ConstraintLocatorBuilder locator,
|
||||
llvm::function_ref<TypeMatchResult()> formUnsolvedResult);
|
||||
|
||||
/// Matches two function result types for a function application. This is
|
||||
/// usually a bind, but also handles e.g IUO unwraps.
|
||||
TypeMatchResult matchFunctionResultTypes(Type expectedResult, Type fnResult,
|
||||
TypeMatchOptions flags,
|
||||
ConstraintLocatorBuilder locator);
|
||||
|
||||
public: // FIXME: public due to statics in CSSimplify.cpp
|
||||
/// Attempt to match up types \c type1 and \c type2, which in effect
|
||||
/// is solving the given type constraint between these two types.
|
||||
@@ -4525,40 +4522,6 @@ public: // FIXME: public due to statics in CSSimplify.cpp
|
||||
}
|
||||
|
||||
public:
|
||||
/// Given a function type where the eventual result type is an optional,
|
||||
/// where "eventual result type" is defined as:
|
||||
/// 1. The result type is an optional
|
||||
/// 2. The result type is a function type with an eventual result
|
||||
/// type that is an optional.
|
||||
///
|
||||
/// return the same function type but with the eventual result type
|
||||
/// replaced by its underlying type.
|
||||
///
|
||||
/// i.e. return (S) -> T for (S) -> T?
|
||||
// return (X) -> () -> Y for (X) -> () -> Y?
|
||||
Type replaceFinalResultTypeWithUnderlying(AnyFunctionType *fnTy) {
|
||||
auto resultTy = fnTy->getResult();
|
||||
if (auto *resultFnTy = resultTy->getAs<AnyFunctionType>())
|
||||
resultTy = replaceFinalResultTypeWithUnderlying(resultFnTy);
|
||||
else {
|
||||
auto objType =
|
||||
resultTy->getWithoutSpecifierType()->getOptionalObjectType();
|
||||
// Preserve l-value through force operation.
|
||||
resultTy =
|
||||
resultTy->is<LValueType>() ? LValueType::get(objType) : objType;
|
||||
}
|
||||
|
||||
assert(resultTy);
|
||||
|
||||
if (auto *genericFn = fnTy->getAs<GenericFunctionType>()) {
|
||||
return GenericFunctionType::get(genericFn->getGenericSignature(),
|
||||
genericFn->getParams(), resultTy,
|
||||
genericFn->getExtInfo());
|
||||
}
|
||||
|
||||
return FunctionType::get(fnTy->getParams(), resultTy, fnTy->getExtInfo());
|
||||
}
|
||||
|
||||
// Build a disjunction that attempts both T? and T for a particular
|
||||
// type binding. The choice of T? is preferred, and we will not
|
||||
// attempt T if we can type check with T?
|
||||
|
||||
@@ -60,6 +60,16 @@ enum class OverloadChoiceKind : int {
|
||||
TupleIndex,
|
||||
};
|
||||
|
||||
/// The kind of implicitly unwrapped optional for an overload reference.
|
||||
enum class IUOReferenceKind : uint8_t {
|
||||
/// This overload references an IUO value which may be directly unwrapped.
|
||||
Value,
|
||||
|
||||
/// This overload references a function, the return value of which may be
|
||||
/// unwrapped.
|
||||
ReturnValue,
|
||||
};
|
||||
|
||||
/// Describes a particular choice within an overload set.
|
||||
///
|
||||
class OverloadChoice {
|
||||
@@ -258,11 +268,11 @@ public:
|
||||
return isDecl() ? getDecl() : nullptr;
|
||||
}
|
||||
|
||||
/// Returns true if this is either a decl for an optional that was
|
||||
/// declared as one that can be implicitly unwrapped, or is a
|
||||
/// function-typed decl that has a return value that is implicitly
|
||||
/// unwrapped.
|
||||
bool isImplicitlyUnwrappedValueOrReturnValue() const;
|
||||
/// Retrieve the type of implicitly unwrapped optional for a reference to this
|
||||
/// overload choice, or \c None if the choice is not for an IUO decl.
|
||||
Optional<IUOReferenceKind>
|
||||
getIUOReferenceKind(ConstraintSystem &cs,
|
||||
bool forSecondApplication = false) const;
|
||||
|
||||
bool isKeyPathDynamicMemberLookup() const {
|
||||
return getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup;
|
||||
|
||||
@@ -554,11 +554,6 @@ namespace {
|
||||
ConstraintLocatorBuilder locator,
|
||||
Optional<Pattern*> typeFromPattern = None);
|
||||
|
||||
/// Coerce an expression of implicitly unwrapped optional type to its
|
||||
/// underlying value type, in the correct way for an implicit
|
||||
/// look-through.
|
||||
Expr *coerceImplicitlyUnwrappedOptionalToValue(Expr *expr, Type objTy);
|
||||
|
||||
/// Peephole an array upcast.
|
||||
void peepholeArrayUpcast(ArrayExpr *expr, Type toType, bool bridged,
|
||||
Type elementType,
|
||||
@@ -758,7 +753,7 @@ namespace {
|
||||
refExpr = declRefExpr;
|
||||
}
|
||||
|
||||
return forceUnwrapIfExpected(refExpr, choice, locator);
|
||||
return forceUnwrapIfExpected(refExpr, locator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -786,7 +781,7 @@ namespace {
|
||||
new (ctx) DeclRefExpr(ref, loc, implicit, semantics, fullType);
|
||||
cs.cacheType(declRefExpr);
|
||||
declRefExpr->setFunctionRefKind(choice.getFunctionRefKind());
|
||||
Expr *result = forceUnwrapIfExpected(declRefExpr, choice, locator);
|
||||
Expr *result = forceUnwrapIfExpected(declRefExpr, locator);
|
||||
|
||||
if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl)) {
|
||||
if (AnyFunctionRef(fnDecl).hasExternalPropertyWrapperParameters() &&
|
||||
@@ -1443,7 +1438,7 @@ namespace {
|
||||
ref->setFunctionRefKind(choice.getFunctionRefKind());
|
||||
auto *DSBI = cs.cacheType(new (context) DotSyntaxBaseIgnoredExpr(
|
||||
base, dotLoc, ref, cs.getType(ref)));
|
||||
return forceUnwrapIfExpected(DSBI, choice, memberLocator);
|
||||
return forceUnwrapIfExpected(DSBI, memberLocator);
|
||||
}
|
||||
|
||||
const bool isUnboundInstanceMember =
|
||||
@@ -1572,27 +1567,10 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
if (isDynamic) {
|
||||
// Rewrite for implicit unwrapping if the solution requires it.
|
||||
auto *dynamicLocator =
|
||||
cs.getConstraintLocator(memberLocator.withPathElement(
|
||||
ConstraintLocator::DynamicLookupResult));
|
||||
|
||||
if (solution.getDisjunctionChoice(dynamicLocator)) {
|
||||
auto *forceValue =
|
||||
new (context) ForceValueExpr(ref, ref->getEndLoc());
|
||||
auto optTy = cs.getType(forceValue->getSubExpr());
|
||||
cs.setType(forceValue, optTy->getOptionalObjectType());
|
||||
ref = forceValue;
|
||||
}
|
||||
}
|
||||
|
||||
// We also need to handle the implicitly unwrap of the result
|
||||
// of the called function if that's the type checking solution
|
||||
// we ended up with.
|
||||
return forceUnwrapIfExpected(
|
||||
ref, choice, memberLocator,
|
||||
member->getAttrs().hasAttribute<OptionalAttr>());
|
||||
return forceUnwrapIfExpected(ref, memberLocator);
|
||||
}
|
||||
|
||||
// For properties, build member references.
|
||||
@@ -1635,7 +1613,7 @@ namespace {
|
||||
result, conversionTy));
|
||||
}
|
||||
}
|
||||
return forceUnwrapIfExpected(result, choice, memberLocator);
|
||||
return forceUnwrapIfExpected(result, memberLocator);
|
||||
}
|
||||
|
||||
if (member->getInterfaceType()->hasDynamicSelfType())
|
||||
@@ -1802,13 +1780,13 @@ namespace {
|
||||
// The thunk that is built for a 'super' method reference does not
|
||||
// require application.
|
||||
if (isSuperPartialApplication) {
|
||||
return forceUnwrapIfExpected(ref, choice, memberLocator);
|
||||
return forceUnwrapIfExpected(ref, memberLocator);
|
||||
}
|
||||
|
||||
ApplyExpr *apply;
|
||||
if (isa<ConstructorDecl>(member)) {
|
||||
// FIXME: Provide type annotation.
|
||||
ref = forceUnwrapIfExpected(ref, choice, memberLocator);
|
||||
ref = forceUnwrapIfExpected(ref, memberLocator);
|
||||
apply = ConstructorRefCallExpr::create(context, ref, base);
|
||||
} else if (isUnboundInstanceMember) {
|
||||
auto refType = cs.simplifyType(openedType);
|
||||
@@ -1823,11 +1801,11 @@ namespace {
|
||||
cs.getType(ref));
|
||||
cs.cacheType(result);
|
||||
closeExistential(result, locator, /*force=*/openedExistential);
|
||||
return forceUnwrapIfExpected(result, choice, memberLocator);
|
||||
return forceUnwrapIfExpected(result, memberLocator);
|
||||
} else {
|
||||
assert((!baseIsInstance || member->isInstanceMember()) &&
|
||||
"can't call a static method on an instance");
|
||||
ref = forceUnwrapIfExpected(ref, choice, memberLocator);
|
||||
ref = forceUnwrapIfExpected(ref, memberLocator);
|
||||
apply = DotSyntaxCallExpr::create(context, ref, dotLoc, base);
|
||||
if (Implicit) {
|
||||
apply->setImplicit();
|
||||
@@ -1968,45 +1946,15 @@ namespace {
|
||||
/// \param locator The locator used to refer to the subscript.
|
||||
/// \param isImplicit Whether this is an implicit subscript.
|
||||
Expr *buildSubscript(Expr *base, ArgumentList *args,
|
||||
ConstraintLocatorBuilder locator, bool isImplicit,
|
||||
AccessSemantics semantics,
|
||||
ConstraintLocatorBuilder locator,
|
||||
ConstraintLocatorBuilder memberLocator,
|
||||
bool isImplicit, AccessSemantics semantics,
|
||||
const SelectedOverload &selected) {
|
||||
// Build the new subscript.
|
||||
auto newSubscript = buildSubscriptHelper(base, args, selected,
|
||||
locator, isImplicit, semantics);
|
||||
|
||||
if (selected.choice.getKind() == OverloadChoiceKind::DeclViaDynamic) {
|
||||
// Rewrite for implicit unwrapping if the solution requires it.
|
||||
auto *dynamicLocator = cs.getConstraintLocator(
|
||||
locator, {ConstraintLocator::SubscriptMember,
|
||||
ConstraintLocator::DynamicLookupResult});
|
||||
|
||||
if (solution.getDisjunctionChoice(dynamicLocator)) {
|
||||
auto *forceValue = new (cs.getASTContext())
|
||||
ForceValueExpr(newSubscript, newSubscript->getEndLoc());
|
||||
auto optTy = cs.getType(forceValue->getSubExpr());
|
||||
cs.setType(forceValue, optTy->getOptionalObjectType());
|
||||
newSubscript = forceValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected.choice.isDecl()) {
|
||||
auto locatorKind = ConstraintLocator::SubscriptMember;
|
||||
if (selected.choice.getKind() ==
|
||||
OverloadChoiceKind::DynamicMemberLookup)
|
||||
locatorKind = ConstraintLocator::Member;
|
||||
|
||||
if (selected.choice.getKind() ==
|
||||
OverloadChoiceKind::KeyPathDynamicMemberLookup &&
|
||||
!isExpr<SubscriptExpr>(locator.getAnchor()))
|
||||
locatorKind = ConstraintLocator::Member;
|
||||
|
||||
newSubscript =
|
||||
forceUnwrapIfExpected(newSubscript, selected.choice,
|
||||
locator.withPathElement(locatorKind));
|
||||
}
|
||||
|
||||
return newSubscript;
|
||||
return forceUnwrapIfExpected(newSubscript, memberLocator,
|
||||
IUOReferenceKind::ReturnValue);
|
||||
}
|
||||
|
||||
Expr *buildSubscriptHelper(Expr *base, ArgumentList *args,
|
||||
@@ -2113,22 +2061,10 @@ namespace {
|
||||
// Check whether the base is 'super'.
|
||||
bool isSuper = base->isSuperExpr();
|
||||
|
||||
// Use the correct locator kind based on the subscript kind.
|
||||
auto locatorKind = ConstraintLocator::SubscriptMember;
|
||||
if (choice.getKind() == OverloadChoiceKind::DynamicMemberLookup)
|
||||
locatorKind = ConstraintLocator::Member;
|
||||
|
||||
if (choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup) {
|
||||
locatorKind = isExpr<SubscriptExpr>(locator.getAnchor())
|
||||
? ConstraintLocator::SubscriptMember
|
||||
: ConstraintLocator::Member;
|
||||
}
|
||||
|
||||
// If we opened up an existential when performing the subscript, open
|
||||
// the base accordingly.
|
||||
auto memberLoc = locator.withPathElement(locatorKind);
|
||||
auto knownOpened = solution.OpenedExistentialTypes.find(
|
||||
cs.getConstraintLocator(memberLoc));
|
||||
auto memberLoc = cs.getCalleeLocator(cs.getConstraintLocator(locator));
|
||||
auto knownOpened = solution.OpenedExistentialTypes.find(memberLoc);
|
||||
if (knownOpened != solution.OpenedExistentialTypes.end()) {
|
||||
base = openExistentialReference(base, knownOpened->second, subscript);
|
||||
baseTy = knownOpened->second;
|
||||
@@ -2142,7 +2078,8 @@ namespace {
|
||||
->castTo<FunctionType>();
|
||||
auto fullSubscriptTy = openedFullFnType->getResult()
|
||||
->castTo<FunctionType>();
|
||||
auto &appliedWrappers = solution.appliedPropertyWrappers[memberLoc.getAnchor()];
|
||||
auto &appliedWrappers =
|
||||
solution.appliedPropertyWrappers[memberLoc->getAnchor()];
|
||||
args = coerceCallArguments(
|
||||
args, fullSubscriptTy, subscriptRef, nullptr,
|
||||
locator.withPathElement(ConstraintLocator::ApplyArgument),
|
||||
@@ -2972,53 +2909,61 @@ namespace {
|
||||
return expr;
|
||||
}
|
||||
|
||||
// Add a forced unwrap of an expression which either has type Optional<T>
|
||||
// or is a function that returns an Optional<T>. The latter turns into a
|
||||
// conversion expression that we will hoist above the ApplyExpr
|
||||
// that needs to be forced during the process of rewriting the expression.
|
||||
//
|
||||
// forForcedOptional is used to indicate that we will further need
|
||||
// to hoist this result above an explicit force of an optional that is
|
||||
// in place for something like an @optional protocol member from
|
||||
// Objective C that we might otherwise mistake for the thing we mean to
|
||||
// force here.
|
||||
Expr *forceUnwrapResult(Expr *expr, bool forForcedOptional =false) {
|
||||
auto ty = simplifyType(cs.getType(expr));
|
||||
/// Add an implicit force unwrap of an expression that references an
|
||||
/// implicitly unwrapped optional T!.
|
||||
Expr *forceUnwrapIUO(Expr *expr) {
|
||||
auto optTy = cs.getType(expr);
|
||||
auto objectTy = optTy->getWithoutSpecifierType()->getOptionalObjectType();
|
||||
assert(objectTy && "Trying to unwrap non-optional?");
|
||||
|
||||
if (forForcedOptional)
|
||||
ty = ty->getOptionalObjectType();
|
||||
// Preserve l-valueness of the result.
|
||||
if (optTy->is<LValueType>())
|
||||
objectTy = LValueType::get(objectTy);
|
||||
|
||||
if (auto *fnTy = ty->getAs<AnyFunctionType>()) {
|
||||
auto underlyingType = cs.replaceFinalResultTypeWithUnderlying(fnTy);
|
||||
expr = new (cs.getASTContext()) ForceValueExpr(expr, expr->getEndLoc(),
|
||||
/*forcedIUO*/ true);
|
||||
cs.setType(expr, objectTy);
|
||||
expr->setImplicit();
|
||||
return expr;
|
||||
}
|
||||
|
||||
auto &ctx = cs.getASTContext();
|
||||
return cs.cacheType(new (ctx) ImplicitlyUnwrappedFunctionConversionExpr(
|
||||
expr, underlyingType));
|
||||
} else {
|
||||
return coerceImplicitlyUnwrappedOptionalToValue(
|
||||
expr, ty->getWithoutSpecifierType()->getOptionalObjectType());
|
||||
/// Retrieve the number of implicit force unwraps required for an implicitly
|
||||
/// unwrapped optional reference at a given locator.
|
||||
unsigned getIUOForceUnwrapCount(ConstraintLocatorBuilder locator,
|
||||
IUOReferenceKind refKind) {
|
||||
// Adjust the locator depending on the type of IUO reference.
|
||||
auto loc = locator;
|
||||
switch (refKind) {
|
||||
case IUOReferenceKind::ReturnValue:
|
||||
loc = locator.withPathElement(ConstraintLocator::FunctionResult);
|
||||
break;
|
||||
case IUOReferenceKind::Value:
|
||||
break;
|
||||
}
|
||||
auto *iuoLocator = cs.getConstraintLocator(
|
||||
loc, {ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice});
|
||||
auto *dynamicLocator = cs.getConstraintLocator(
|
||||
loc, {ConstraintLocator::DynamicLookupResult});
|
||||
|
||||
// First check whether we recorded an implicit unwrap for an IUO.
|
||||
unsigned unwrapCount = 0;
|
||||
if (solution.DisjunctionChoices.lookup(iuoLocator))
|
||||
unwrapCount += 1;
|
||||
|
||||
// Next check if we recorded an implicit unwrap for dynamic lookup.
|
||||
if (solution.DisjunctionChoices.lookup(dynamicLocator))
|
||||
unwrapCount += 1;
|
||||
|
||||
return unwrapCount;
|
||||
}
|
||||
|
||||
bool shouldForceUnwrapResult(OverloadChoice choice,
|
||||
ConstraintLocatorBuilder locator) {
|
||||
if (!choice.isImplicitlyUnwrappedValueOrReturnValue())
|
||||
return false;
|
||||
|
||||
auto *choiceLocator = cs.getConstraintLocator(locator.withPathElement(
|
||||
ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice));
|
||||
|
||||
return solution.getDisjunctionChoice(choiceLocator);
|
||||
}
|
||||
|
||||
Expr *forceUnwrapIfExpected(Expr *expr, OverloadChoice choice,
|
||||
ConstraintLocatorBuilder locator,
|
||||
bool forForcedOptional = false) {
|
||||
if (!shouldForceUnwrapResult(choice, locator))
|
||||
return expr;
|
||||
|
||||
// Force the expression if required for the solution.
|
||||
return forceUnwrapResult(expr, forForcedOptional);
|
||||
Expr *
|
||||
forceUnwrapIfExpected(Expr *expr, ConstraintLocatorBuilder locator,
|
||||
IUOReferenceKind refKind = IUOReferenceKind::Value) {
|
||||
auto unwrapCount = getIUOForceUnwrapCount(locator, refKind);
|
||||
for (unsigned i = 0; i < unwrapCount; ++i)
|
||||
expr = forceUnwrapIUO(expr);
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr *visitDeclRefExpr(DeclRefExpr *expr) {
|
||||
@@ -3401,8 +3346,8 @@ namespace {
|
||||
ArgumentList::forImplicitSingle(ctx, ctx.Id_dynamicMember, argExpr);
|
||||
// Build and return a subscript that uses this string as the index.
|
||||
return buildSubscript(base, argList, cs.getConstraintLocator(expr),
|
||||
/*isImplicit*/ true, AccessSemantics::Ordinary,
|
||||
overload);
|
||||
memberLocator, /*isImplicit*/ true,
|
||||
AccessSemantics::Ordinary, overload);
|
||||
}
|
||||
|
||||
Type getTypeOfDynamicMemberIndex(const SelectedOverload &overload) {
|
||||
@@ -3534,8 +3479,9 @@ namespace {
|
||||
}
|
||||
|
||||
return buildSubscript(expr->getBase(), expr->getArgs(),
|
||||
cs.getConstraintLocator(expr), expr->isImplicit(),
|
||||
expr->getAccessSemantics(), *overload);
|
||||
cs.getConstraintLocator(expr), memberLocator,
|
||||
expr->isImplicit(), expr->getAccessSemantics(),
|
||||
*overload);
|
||||
}
|
||||
|
||||
/// "Finish" an array expression by filling in the semantic expression.
|
||||
@@ -3633,8 +3579,8 @@ namespace {
|
||||
auto *memberLocator =
|
||||
cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember);
|
||||
return buildSubscript(expr->getBase(), expr->getArgs(),
|
||||
cs.getConstraintLocator(expr), expr->isImplicit(),
|
||||
AccessSemantics::Ordinary,
|
||||
cs.getConstraintLocator(expr), memberLocator,
|
||||
expr->isImplicit(), AccessSemantics::Ordinary,
|
||||
solution.getOverloadChoice(memberLocator));
|
||||
}
|
||||
|
||||
@@ -4139,8 +4085,7 @@ namespace {
|
||||
if (!coerced)
|
||||
return nullptr;
|
||||
|
||||
return coerceImplicitlyUnwrappedOptionalToValue(
|
||||
coerced, cs.getType(coerced)->getOptionalObjectType());
|
||||
return forceUnwrapIUO(coerced);
|
||||
}
|
||||
|
||||
return visitCoerceExpr(expr, None);
|
||||
@@ -4294,8 +4239,7 @@ namespace {
|
||||
if (!coerced)
|
||||
return nullptr;
|
||||
|
||||
return coerceImplicitlyUnwrappedOptionalToValue(
|
||||
coerced, cs.getType(coerced)->getOptionalObjectType());
|
||||
return forceUnwrapIUO(coerced);
|
||||
}
|
||||
|
||||
return handleConditionalCheckedCastExpr(expr, castTypeRepr);
|
||||
@@ -4447,42 +4391,7 @@ namespace {
|
||||
}
|
||||
|
||||
Expr *visitForceValueExpr(ForceValueExpr *expr) {
|
||||
// Check to see if we are forcing an
|
||||
// ImplicitlyUnwrappedFunctionConversionExpr. This can happen
|
||||
// in cases where we had a ForceValueExpr of an optional for a
|
||||
// declaration for a function whose result type we need to
|
||||
// implicitly force after applying. We need to hoist the function
|
||||
// conversion above the ForceValueExpr, so that we may ultimately
|
||||
// hoist it above the ApplyExpr where we will eventually rewrite the
|
||||
// function conversion into a force of the result.
|
||||
Expr *replacement = expr;
|
||||
if (auto fnConv =
|
||||
dyn_cast<ImplicitlyUnwrappedFunctionConversionExpr>(expr->getSubExpr())) {
|
||||
auto fnConvSubExpr = fnConv->getSubExpr();
|
||||
auto fnConvSubObjTy =
|
||||
cs.getType(fnConvSubExpr)->getOptionalObjectType();
|
||||
cs.setType(expr, fnConvSubObjTy);
|
||||
expr->setSubExpr(fnConvSubExpr);
|
||||
fnConv->setSubExpr(expr);
|
||||
replacement = fnConv;
|
||||
}
|
||||
|
||||
Type valueType = simplifyType(cs.getType(expr));
|
||||
cs.setType(expr, valueType);
|
||||
|
||||
// Coerce the object type, if necessary.
|
||||
auto subExpr = expr->getSubExpr();
|
||||
if (auto objectTy = cs.getType(subExpr)->getOptionalObjectType()) {
|
||||
if (objectTy && !objectTy->isEqual(valueType)) {
|
||||
auto coercedSubExpr = coerceToType(subExpr,
|
||||
OptionalType::get(valueType),
|
||||
cs.getConstraintLocator(subExpr));
|
||||
|
||||
expr->setSubExpr(coercedSubExpr);
|
||||
}
|
||||
}
|
||||
|
||||
return replacement;
|
||||
return simplifyExprType(expr);
|
||||
}
|
||||
|
||||
Expr *visitOpenExistentialExpr(OpenExistentialExpr *expr) {
|
||||
@@ -5151,7 +5060,9 @@ namespace {
|
||||
fieldIndex, resolvedTy, componentLoc));
|
||||
}
|
||||
|
||||
if (shouldForceUnwrapResult(overload.choice, locator))
|
||||
auto unwrapCount =
|
||||
getIUOForceUnwrapCount(locator, IUOReferenceKind::Value);
|
||||
for (unsigned i = 0; i < unwrapCount; ++i)
|
||||
buildKeyPathOptionalForceComponent(components);
|
||||
}
|
||||
|
||||
@@ -5227,7 +5138,9 @@ namespace {
|
||||
ctx, ref, args, resolvedTy, ctx.AllocateCopy(conformances));
|
||||
components.push_back(comp);
|
||||
|
||||
if (shouldForceUnwrapResult(overload.choice, memberLoc))
|
||||
auto unwrapCount =
|
||||
getIUOForceUnwrapCount(memberLoc, IUOReferenceKind::ReturnValue);
|
||||
for (unsigned i = 0; i < unwrapCount; ++i)
|
||||
buildKeyPathOptionalForceComponent(components);
|
||||
}
|
||||
|
||||
@@ -5673,19 +5586,6 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType,
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr *ExprRewriter::coerceImplicitlyUnwrappedOptionalToValue(Expr *expr, Type objTy) {
|
||||
auto optTy = cs.getType(expr);
|
||||
// Preserve l-valueness of the result.
|
||||
if (optTy->is<LValueType>())
|
||||
objTy = LValueType::get(objTy);
|
||||
|
||||
expr = new (cs.getASTContext()) ForceValueExpr(expr, expr->getEndLoc(),
|
||||
/* forcedIUO=*/ true);
|
||||
cs.setType(expr, objTy);
|
||||
expr->setImplicit();
|
||||
return expr;
|
||||
}
|
||||
|
||||
/// Determine whether the given expression is a reference to an
|
||||
/// unbound instance member of a type.
|
||||
static bool isReferenceToMetatypeMember(ConstraintSystem &cs, Expr *expr) {
|
||||
@@ -7679,12 +7579,6 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
|
||||
return special;
|
||||
}
|
||||
}
|
||||
|
||||
bool unwrapResult = false;
|
||||
if (auto *IUOFnTy = dyn_cast<ImplicitlyUnwrappedFunctionConversionExpr>(fn)) {
|
||||
unwrapResult = true;
|
||||
fn = IUOFnTy->getSubExpr();
|
||||
}
|
||||
|
||||
// If we're applying a function that resulted from a covariant
|
||||
// function conversion, strip off that conversion.
|
||||
@@ -7742,14 +7636,17 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
|
||||
// Try closing the existential, if there is one.
|
||||
closeExistential(result, locator);
|
||||
|
||||
if (unwrapResult)
|
||||
return forceUnwrapResult(result);
|
||||
|
||||
// We may also need to force the result for an IUO. We don't apply this on
|
||||
// SelfApplyExprs, as the force unwraps should be inserted at the result of
|
||||
// main application, not on the curried member reference.
|
||||
if (!isa<SelfApplyExpr>(apply)) {
|
||||
result = forceUnwrapIfExpected(result, calleeLocator,
|
||||
IUOReferenceKind::ReturnValue);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// FIXME: handle unwrapping everywhere else
|
||||
assert(!unwrapResult);
|
||||
// FIXME: Handle unwrapping everywhere else.
|
||||
|
||||
// If this is an UnresolvedType in the system, preserve it.
|
||||
if (cs.getType(fn)->is<UnresolvedType>()) {
|
||||
|
||||
@@ -1517,6 +1517,68 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
|
||||
return cs.getTypeMatchSuccess();
|
||||
}
|
||||
|
||||
ConstraintSystem::TypeMatchResult
|
||||
ConstraintSystem::matchFunctionResultTypes(Type expectedResult, Type fnResult,
|
||||
TypeMatchOptions flags,
|
||||
ConstraintLocatorBuilder locator) {
|
||||
// If we have a callee with an IUO return, add a disjunction that can either
|
||||
// bind to the result or an unwrapped result.
|
||||
auto *calleeLoc = getCalleeLocator(getConstraintLocator(locator));
|
||||
auto *calleeResultLoc = getConstraintLocator(
|
||||
calleeLoc, ConstraintLocator::FunctionResult);
|
||||
auto selected = findSelectedOverloadFor(calleeLoc);
|
||||
|
||||
// If we don't have a direct callee, this might be the second application
|
||||
// of a curried function reference, in which case we need to dig into the
|
||||
// inner call to find the callee.
|
||||
// FIXME: This is a bit of a hack. We should consider rewriting curried
|
||||
// applies as regular applies in PreCheckExpr to eliminate the need to special
|
||||
// case double applies in the solver.
|
||||
bool isSecondApply = false;
|
||||
if (!selected) {
|
||||
auto anchor = locator.getAnchor();
|
||||
if (auto *callExpr = getAsExpr<CallExpr>(anchor)) {
|
||||
if (auto *innerCall = getAsExpr<CallExpr>(callExpr->getSemanticFn())) {
|
||||
auto *innerCalleeLoc =
|
||||
getCalleeLocator(getConstraintLocator(innerCall));
|
||||
if (auto innerOverload = findSelectedOverloadFor(innerCalleeLoc)) {
|
||||
auto choice = innerOverload->choice;
|
||||
if (choice.getFunctionRefKind() == FunctionRefKind::DoubleApply) {
|
||||
isSecondApply = true;
|
||||
selected.emplace(*innerOverload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
auto choice = selected->choice;
|
||||
|
||||
// Subscripts found through dynamic lookup need special treatment. Unlike
|
||||
// other decls found through dynamic lookup, they cannot have an optional
|
||||
// applied to their reference, instead it's applied to their result. As
|
||||
// such, we may need to unwrap another level of optionality.
|
||||
if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic &&
|
||||
isa<SubscriptDecl>(choice.getDecl())) {
|
||||
// Introduce a type variable to record whether we needed to unwrap the
|
||||
// outer optional.
|
||||
auto outerTy = createTypeVariable(calleeResultLoc, TVO_CanBindToLValue);
|
||||
buildDisjunctionForDynamicLookupResult(outerTy, fnResult,
|
||||
calleeResultLoc);
|
||||
fnResult = outerTy;
|
||||
}
|
||||
|
||||
auto iuoKind = choice.getIUOReferenceKind(*this, isSecondApply);
|
||||
if (iuoKind == IUOReferenceKind::ReturnValue) {
|
||||
buildDisjunctionForImplicitlyUnwrappedOptional(expectedResult, fnResult,
|
||||
calleeResultLoc);
|
||||
return getTypeMatchSuccess();
|
||||
}
|
||||
}
|
||||
return matchTypes(expectedResult, fnResult, ConstraintKind::Bind, flags,
|
||||
locator);
|
||||
}
|
||||
|
||||
ConstraintSystem::TypeMatchResult
|
||||
ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
|
||||
ConstraintKind kind, TypeMatchOptions flags,
|
||||
@@ -8371,9 +8433,11 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
|
||||
// implicit optional, don't try to fix it. The IUO will be forced instead.
|
||||
if (auto dotExpr = getAsExpr<UnresolvedDotExpr>(locator->getAnchor())) {
|
||||
auto baseExpr = dotExpr->getBase();
|
||||
if (auto overload = findSelectedOverloadFor(baseExpr))
|
||||
if (overload->choice.isImplicitlyUnwrappedValueOrReturnValue())
|
||||
if (auto overload = findSelectedOverloadFor(baseExpr)) {
|
||||
auto iuoKind = overload->choice.getIUOReferenceKind(*this);
|
||||
if (iuoKind == IUOReferenceKind::Value)
|
||||
return SolutionKind::Error;
|
||||
}
|
||||
}
|
||||
|
||||
// Let's check whether the problem is related to optionality of base
|
||||
@@ -10286,26 +10350,6 @@ ConstraintSystem::simplifyApplicableFnConstraint(
|
||||
// Track how many times we do this so that we can record a fix for each.
|
||||
++unwrapCount;
|
||||
}
|
||||
|
||||
// Let's account for optional members concept from Objective-C
|
||||
// which forms a disjunction for member type to check whether
|
||||
// it would be possible to use optional type directly or it has
|
||||
// to be force unwrapped (because such types are imported as IUO).
|
||||
if (unwrapCount > 0 && desugar2->is<TypeVariableType>()) {
|
||||
auto *typeVar = desugar2->castTo<TypeVariableType>();
|
||||
auto *locator = typeVar->getImpl().getLocator();
|
||||
if (locator->isLastElement<LocatorPathElt::Member>()) {
|
||||
auto *fix = ForceOptional::create(*this, origType2, desugar2,
|
||||
getConstraintLocator(locator));
|
||||
if (recordFix(fix, /*impact=*/unwrapCount))
|
||||
return SolutionKind::Error;
|
||||
|
||||
// Since the right-hand side of the constraint has been changed
|
||||
// we have to re-generate this constraint to use new type.
|
||||
flags |= TMF_GenerateConstraints;
|
||||
return formUnsolved();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For a function, bind the output and convert the argument to the input.
|
||||
@@ -10348,12 +10392,10 @@ ConstraintSystem::simplifyApplicableFnConstraint(
|
||||
}
|
||||
|
||||
// The result types are equivalent.
|
||||
if (matchTypes(func1->getResult(),
|
||||
func2->getResult(),
|
||||
ConstraintKind::Bind,
|
||||
subflags,
|
||||
locator.withPathElement(
|
||||
ConstraintLocator::FunctionResult)).isFailure())
|
||||
if (matchFunctionResultTypes(
|
||||
func1->getResult(), func2->getResult(), subflags,
|
||||
locator.withPathElement(ConstraintLocator::FunctionResult))
|
||||
.isFailure())
|
||||
return SolutionKind::Error;
|
||||
|
||||
if (unwrapCount == 0)
|
||||
@@ -10622,12 +10664,10 @@ ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint(
|
||||
return SolutionKind::Error;
|
||||
|
||||
// The result types are equivalent.
|
||||
if (matchTypes(func1->getResult(),
|
||||
func2->getResult(),
|
||||
ConstraintKind::Bind,
|
||||
subflags,
|
||||
locator.withPathElement(
|
||||
ConstraintLocator::FunctionResult)).isFailure())
|
||||
if (matchFunctionResultTypes(
|
||||
func1->getResult(), func2->getResult(), subflags,
|
||||
locator.withPathElement(ConstraintLocator::FunctionResult))
|
||||
.isFailure())
|
||||
return SolutionKind::Error;
|
||||
|
||||
return SolutionKind::Solved;
|
||||
|
||||
@@ -719,6 +719,9 @@ private:
|
||||
// from currently selected representative.
|
||||
void pruneOverloadSet(Constraint *disjunction) {
|
||||
auto *choice = disjunction->getNestedConstraints().front();
|
||||
if (choice->getKind() != ConstraintKind::BindOverload)
|
||||
return;
|
||||
|
||||
auto *typeVar = choice->getFirstType()->getAs<TypeVariableType>();
|
||||
if (!typeVar)
|
||||
return;
|
||||
|
||||
@@ -1699,6 +1699,13 @@ ConstraintSystem::getTypeOfMemberReference(
|
||||
|
||||
AnyFunctionType *funcType;
|
||||
|
||||
// Check if we need to apply a layer of optionality to the resulting type.
|
||||
auto isReferenceOptional = false;
|
||||
if (!isRequirementOrWitness(locator)) {
|
||||
if (isDynamicResult || value->getAttrs().hasAttribute<OptionalAttr>())
|
||||
isReferenceOptional = true;
|
||||
}
|
||||
|
||||
if (isa<AbstractFunctionDecl>(value) ||
|
||||
isa<EnumElementDecl>(value)) {
|
||||
// This is the easy case.
|
||||
@@ -1718,14 +1725,11 @@ ConstraintSystem::getTypeOfMemberReference(
|
||||
if (doesStorageProduceLValue(subscript, baseTy, useDC, locator))
|
||||
elementTy = LValueType::get(elementTy);
|
||||
|
||||
// See ConstraintSystem::resolveOverload() -- optional and dynamic
|
||||
// subscripts are a special case, because the optionality is
|
||||
// applied to the result type and not the type of the reference.
|
||||
if (!isRequirementOrWitness(locator)) {
|
||||
if (subscript->getAttrs().hasAttribute<OptionalAttr>() ||
|
||||
isDynamicResult)
|
||||
elementTy = OptionalType::get(elementTy->getRValueType());
|
||||
}
|
||||
// Optional and dynamic subscripts are a special case, because the
|
||||
// optionality is applied to the result type and not the type of the
|
||||
// reference.
|
||||
if (isReferenceOptional)
|
||||
elementTy = OptionalType::get(elementTy->getRValueType());
|
||||
|
||||
auto indices = subscript->getInterfaceType()
|
||||
->castTo<AnyFunctionType>()->getParams();
|
||||
@@ -1923,6 +1927,10 @@ ConstraintSystem::getTypeOfMemberReference(
|
||||
}
|
||||
}
|
||||
|
||||
// If we need to wrap the type in an optional, do so now.
|
||||
if (isReferenceOptional && !isa<SubscriptDecl>(value))
|
||||
type = OptionalType::get(type->getRValueType());
|
||||
|
||||
// If we opened up any type variables, record the replacements.
|
||||
recordOpenedTypes(locator, replacements);
|
||||
|
||||
@@ -2279,128 +2287,6 @@ isInvalidPartialApplication(ConstraintSystem &cs,
|
||||
return {true, level};
|
||||
}
|
||||
|
||||
std::pair<Type, bool> ConstraintSystem::adjustTypeOfOverloadReference(
|
||||
const OverloadChoice &choice, ConstraintLocator *locator,
|
||||
Type boundType, Type refType) {
|
||||
// If the declaration is unavailable, note that in the score.
|
||||
if (isDeclUnavailable(choice.getDecl(), locator))
|
||||
increaseScore(SK_Unavailable);
|
||||
|
||||
bool bindConstraintCreated = false;
|
||||
const auto kind = choice.getKind();
|
||||
if (kind != OverloadChoiceKind::DeclViaDynamic &&
|
||||
!isRequirementOrWitness(locator) &&
|
||||
choice.getDecl()->getAttrs().hasAttribute<OptionalAttr>() &&
|
||||
!isa<SubscriptDecl>(choice.getDecl())) {
|
||||
// For a non-subscript declaration that is an optional
|
||||
// requirement in a protocol, strip off the lvalue-ness (FIXME:
|
||||
// one cannot assign to such declarations for now) and make a
|
||||
// reference to that declaration be optional.
|
||||
//
|
||||
// Subscript declarations are handled within
|
||||
// getTypeOfMemberReference(); their result types are optional.
|
||||
|
||||
// Deal with values declared as implicitly unwrapped, or
|
||||
// functions with return types that are implicitly unwrapped.
|
||||
// TODO: Move this logic to bindOverloadType.
|
||||
if (choice.isImplicitlyUnwrappedValueOrReturnValue()) {
|
||||
// Build the disjunction to attempt binding both T? and T (or
|
||||
// function returning T? and function returning T).
|
||||
Type ty = createTypeVariable(locator,
|
||||
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
|
||||
buildDisjunctionForImplicitlyUnwrappedOptional(ty, refType, locator);
|
||||
addConstraint(ConstraintKind::Bind, boundType,
|
||||
OptionalType::get(ty->getRValueType()), locator);
|
||||
bindConstraintCreated = true;
|
||||
}
|
||||
|
||||
// TODO: Move this to getTypeOfMemberReference.
|
||||
refType = OptionalType::get(refType->getRValueType());
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case OverloadChoiceKind::Decl:
|
||||
case OverloadChoiceKind::DeclViaBridge:
|
||||
case OverloadChoiceKind::DeclViaUnwrappedOptional:
|
||||
case OverloadChoiceKind::TupleIndex:
|
||||
case OverloadChoiceKind::KeyPathApplication:
|
||||
return {refType, bindConstraintCreated};
|
||||
case OverloadChoiceKind::DeclViaDynamic: {
|
||||
// TODO: Move the IUO handling logic here to bindOverloadType.
|
||||
if (isa<SubscriptDecl>(choice.getDecl())) {
|
||||
// We always expect function type for subscripts.
|
||||
auto fnTy = refType->castTo<AnyFunctionType>();
|
||||
if (choice.isImplicitlyUnwrappedValueOrReturnValue()) {
|
||||
auto resultTy = fnTy->getResult();
|
||||
// We expect the element type to be a double-optional.
|
||||
auto optTy = resultTy->getOptionalObjectType();
|
||||
assert(optTy->getOptionalObjectType());
|
||||
|
||||
// For our original type T -> U?? we will generate:
|
||||
// A disjunction V = { U?, U }
|
||||
// and a disjunction boundType = { T -> V?, T -> V }
|
||||
Type ty = createTypeVariable(locator, TVO_CanBindToNoEscape);
|
||||
|
||||
buildDisjunctionForImplicitlyUnwrappedOptional(ty, optTy, locator);
|
||||
|
||||
// Create a new function type with an optional of this type
|
||||
// variable as the result type.
|
||||
if (auto *genFnTy = fnTy->getAs<GenericFunctionType>()) {
|
||||
fnTy = GenericFunctionType::get(
|
||||
genFnTy->getGenericSignature(), genFnTy->getParams(),
|
||||
OptionalType::get(ty), genFnTy->getExtInfo());
|
||||
} else {
|
||||
fnTy = FunctionType::get(fnTy->getParams(), OptionalType::get(ty),
|
||||
fnTy->getExtInfo());
|
||||
}
|
||||
}
|
||||
|
||||
buildDisjunctionForDynamicLookupResult(boundType, fnTy, locator);
|
||||
} else {
|
||||
Type ty = refType;
|
||||
|
||||
// If this is something we need to implicitly unwrap, set up a
|
||||
// new type variable and disjunction that will allow us to make
|
||||
// the choice of whether to do so.
|
||||
if (choice.isImplicitlyUnwrappedValueOrReturnValue()) {
|
||||
// Duplicate the structure of boundType, with fresh type
|
||||
// variables. We'll create a binding disjunction using this,
|
||||
// selecting between options for refType, which is either
|
||||
// Optional or a function type returning Optional.
|
||||
assert(boundType->hasTypeVariable());
|
||||
ty = boundType.transform([this](Type elTy) -> Type {
|
||||
if (auto *tv = dyn_cast<TypeVariableType>(elTy.getPointer())) {
|
||||
return createTypeVariable(tv->getImpl().getLocator(),
|
||||
tv->getImpl().getRawOptions());
|
||||
}
|
||||
return elTy;
|
||||
});
|
||||
|
||||
buildDisjunctionForImplicitlyUnwrappedOptional(
|
||||
ty, refType->getRValueType(), locator);
|
||||
}
|
||||
|
||||
// Build the disjunction to attempt binding both T? and T (or
|
||||
// function returning T? and function returning T).
|
||||
buildDisjunctionForDynamicLookupResult(
|
||||
boundType, OptionalType::get(ty->getRValueType()), locator);
|
||||
|
||||
// We store an Optional of the originally resolved type in the
|
||||
// overload set.
|
||||
// TODO: Move this to getTypeOfMemberReference.
|
||||
refType = OptionalType::get(refType->getRValueType());
|
||||
}
|
||||
|
||||
return {refType, /*bindConstraintCreated*/ true};
|
||||
}
|
||||
case OverloadChoiceKind::DynamicMemberLookup:
|
||||
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
|
||||
return {refType, bindConstraintCreated};
|
||||
}
|
||||
|
||||
llvm_unreachable("Unhandled OverloadChoiceKind in switch.");
|
||||
}
|
||||
|
||||
/// Walk a closure AST to determine its effects.
|
||||
///
|
||||
/// \returns a function's extended info describing the effects, as
|
||||
@@ -2634,15 +2520,15 @@ void ConstraintSystem::buildDisjunctionForOptionalVsUnderlying(
|
||||
Constraint::create(*this, ConstraintKind::Bind, boundTy, ty, locator);
|
||||
bindToOptional->setFavored();
|
||||
|
||||
Type underlyingType;
|
||||
if (auto *fnTy = ty->getAs<AnyFunctionType>())
|
||||
underlyingType = replaceFinalResultTypeWithUnderlying(fnTy);
|
||||
else if (auto *typeVar = rvalueTy->getAs<TypeVariableType>()) {
|
||||
auto underlyingType = rvalueTy->getOptionalObjectType();
|
||||
if (!underlyingType) {
|
||||
// If we don't have an optional, `ty` hasn't been resolved yet.
|
||||
auto *typeVar = rvalueTy->castTo<TypeVariableType>();
|
||||
auto *locator = typeVar->getImpl().getLocator();
|
||||
|
||||
// If `ty` hasn't been resolved yet, we need to allocate a type variable to
|
||||
// represent an object type of a future optional, and add a constraint
|
||||
// between `ty` and `underlyingType` to model it.
|
||||
// We need to allocate a type variable to represent an object type of a
|
||||
// future optional, and add a constraint between `ty` and `underlyingType`
|
||||
// to model it.
|
||||
underlyingType = createTypeVariable(
|
||||
getConstraintLocator(locator, LocatorPathElt::GenericArgument(0)),
|
||||
TVO_PrefersSubtypeBinding | TVO_CanBindToLValue |
|
||||
@@ -2652,12 +2538,7 @@ void ConstraintSystem::buildDisjunctionForOptionalVsUnderlying(
|
||||
// to the underlying type below.
|
||||
addConstraint(ConstraintKind::OptionalObject, typeVar, underlyingType,
|
||||
locator);
|
||||
} else {
|
||||
underlyingType = rvalueTy->getOptionalObjectType();
|
||||
}
|
||||
|
||||
assert(underlyingType);
|
||||
|
||||
if (ty->is<LValueType>())
|
||||
underlyingType = LValueType::get(underlyingType);
|
||||
|
||||
@@ -2681,7 +2562,7 @@ void ConstraintSystem::bindOverloadType(
|
||||
auto openedType = overload.openedType;
|
||||
|
||||
auto bindTypeOrIUO = [&](Type ty) {
|
||||
if (choice.isImplicitlyUnwrappedValueOrReturnValue()) {
|
||||
if (choice.getIUOReferenceKind(*this) == IUOReferenceKind::Value) {
|
||||
// Build the disjunction to attempt binding both T? and T (or
|
||||
// function returning T? and function returning T).
|
||||
buildDisjunctionForImplicitlyUnwrappedOptional(boundType, ty, locator);
|
||||
@@ -2727,20 +2608,25 @@ void ConstraintSystem::bindOverloadType(
|
||||
case OverloadChoiceKind::DeclViaUnwrappedOptional:
|
||||
case OverloadChoiceKind::TupleIndex:
|
||||
case OverloadChoiceKind::KeyPathApplication:
|
||||
case OverloadChoiceKind::DeclViaDynamic:
|
||||
bindTypeOrIUO(openedType);
|
||||
return;
|
||||
case OverloadChoiceKind::DeclViaDynamic: {
|
||||
// Subscripts have optionality applied to their result type rather than
|
||||
// the type of their reference, so there's nothing to adjust here.
|
||||
if (isa<SubscriptDecl>(choice.getDecl())) {
|
||||
bindTypeOrIUO(openedType);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build an outer disjunction to attempt binding both T? and T, then bind
|
||||
// as normal. This is needed to correctly handle e.g IUO properties which
|
||||
// may need two levels of optionality unwrapped T??.
|
||||
auto outerTy = createTypeVariable(locator, TVO_CanBindToLValue);
|
||||
buildDisjunctionForDynamicLookupResult(outerTy, openedType, locator);
|
||||
bindTypeOrIUO(outerTy);
|
||||
return;
|
||||
}
|
||||
case OverloadChoiceKind::DynamicMemberLookup: {
|
||||
// DynamicMemberLookup results are always a (dynamicMember:T1)->T2
|
||||
// subscript.
|
||||
auto refFnType = openedType->castTo<FunctionType>();
|
||||
|
||||
// Before we drop the argument type on the floor, we need to constrain it
|
||||
// to having a literal conformance to ExpressibleByStringLiteral. This
|
||||
// makes the index default to String if otherwise unconstrained.
|
||||
assert(refFnType->getParams().size() == 1 &&
|
||||
"subscript always has one arg");
|
||||
|
||||
auto stringLiteral =
|
||||
TypeChecker::getProtocol(getASTContext(), choice.getDecl()->getLoc(),
|
||||
KnownProtocolKind::ExpressibleByStringLiteral);
|
||||
@@ -2753,8 +2639,7 @@ void ConstraintSystem::bindOverloadType(
|
||||
auto argTy = createTypeVariable(locator, /*options*/ 0);
|
||||
addConstraint(ConstraintKind::LiteralConformsTo, argTy,
|
||||
stringLiteral->getDeclaredInterfaceType(), locator);
|
||||
addDynamicMemberSubscriptConstraints(argTy, refFnType->getResult());
|
||||
bindTypeOrIUO(refFnType->getResult());
|
||||
addDynamicMemberSubscriptConstraints(argTy, /*resultTy*/ boundType);
|
||||
return;
|
||||
}
|
||||
case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
|
||||
@@ -2852,14 +2737,11 @@ void ConstraintSystem::bindOverloadType(
|
||||
ConstraintKind::ApplicableFunction, adjustedFnTy, memberTy,
|
||||
kpLocBuilder.withPathElement(ConstraintLocator::ApplyFunction));
|
||||
|
||||
addConstraint(ConstraintKind::FunctionResult, boundType,
|
||||
originalCallerTy->getResult(), keyPathLoc);
|
||||
|
||||
addConstraint(ConstraintKind::Equal, subscriptResultTy, leafTy,
|
||||
keyPathLoc);
|
||||
|
||||
addDynamicMemberSubscriptConstraints(/*argTy*/ keyPathTy,
|
||||
fnType->getResult());
|
||||
originalCallerTy->getResult());
|
||||
|
||||
// Bind the overload type to the opened type as usual to match the fact
|
||||
// that this is a subscript in the source.
|
||||
@@ -2870,12 +2752,12 @@ void ConstraintSystem::bindOverloadType(
|
||||
// constraint to represent that conversion instead of loading member
|
||||
// type into "leaf" directly.
|
||||
addConstraint(ConstraintKind::Equal, memberTy, leafTy, keyPathLoc);
|
||||
addDynamicMemberSubscriptConstraints(/*argTy*/ keyPathTy,
|
||||
fnType->getResult());
|
||||
|
||||
// Bind the overload type to the result to model the fact that this a
|
||||
// property access in the source.
|
||||
bindTypeOrIUO(fnType->getResult());
|
||||
// Form constraints for a x[dynamicMember:] subscript with a key path
|
||||
// argument, where the overload type is bound to the result to model the
|
||||
// fact that this a property access in the source.
|
||||
addDynamicMemberSubscriptConstraints(/*argTy*/ keyPathTy,
|
||||
boundType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2906,7 +2788,6 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
|
||||
Type refType;
|
||||
Type openedFullType;
|
||||
|
||||
bool bindConstraintCreated = false;
|
||||
switch (auto kind = choice.getKind()) {
|
||||
case OverloadChoiceKind::Decl:
|
||||
case OverloadChoiceKind::DeclViaBridge:
|
||||
@@ -2940,17 +2821,6 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
|
||||
= getTypeOfReference(choice.getDecl(),
|
||||
choice.getFunctionRefKind(), locator, useDC);
|
||||
}
|
||||
|
||||
// For a non-subscript declaration found via dynamic lookup, strip
|
||||
// off the lvalue-ness (FIXME: as a temporary hack. We eventually
|
||||
// want this to work) and make a reference to that declaration be
|
||||
// an implicitly unwrapped optional.
|
||||
//
|
||||
// Subscript declarations are handled within
|
||||
// getTypeOfMemberReference(); their result types are unchecked
|
||||
// optional.
|
||||
std::tie(refType, bindConstraintCreated) =
|
||||
adjustTypeOfOverloadReference(choice, locator, boundType, refType);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3095,11 +2965,9 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
|
||||
assert(result.second && "Already resolved this overload?");
|
||||
(void)result;
|
||||
|
||||
// In some cases we already created the appropriate bind constraints.
|
||||
if (!bindConstraintCreated) {
|
||||
bindOverloadType(overload, boundType, locator, useDC,
|
||||
verifyThatArgumentIsHashable);
|
||||
}
|
||||
// Add the constraints necessary to bind the overload type.
|
||||
bindOverloadType(overload, boundType, locator, useDC,
|
||||
verifyThatArgumentIsHashable);
|
||||
|
||||
if (isDebugMode()) {
|
||||
PrintOptions PO;
|
||||
@@ -3110,10 +2978,14 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
|
||||
<< refType->getString(PO) << ")\n";
|
||||
}
|
||||
|
||||
// If this overload is disfavored, note that.
|
||||
if (choice.isDecl() &&
|
||||
choice.getDecl()->getAttrs().hasAttribute<DisfavoredOverloadAttr>()) {
|
||||
increaseScore(SK_DisfavoredOverload);
|
||||
if (auto *decl = choice.getDeclOrNull()) {
|
||||
// If the declaration is unavailable, note that in the score.
|
||||
if (isDeclUnavailable(decl, locator))
|
||||
increaseScore(SK_Unavailable);
|
||||
|
||||
// If this overload is disfavored, note that.
|
||||
if (decl->getAttrs().hasAttribute<DisfavoredOverloadAttr>())
|
||||
increaseScore(SK_DisfavoredOverload);
|
||||
}
|
||||
|
||||
if (choice.isFallbackMemberOnUnwrappedBase()) {
|
||||
@@ -3265,27 +3137,37 @@ DeclName OverloadChoice::getName() const {
|
||||
llvm_unreachable("Unhandled OverloadChoiceKind in switch.");
|
||||
}
|
||||
|
||||
bool OverloadChoice::isImplicitlyUnwrappedValueOrReturnValue() const {
|
||||
if (!isDecl())
|
||||
return false;
|
||||
Optional<IUOReferenceKind>
|
||||
OverloadChoice::getIUOReferenceKind(ConstraintSystem &cs,
|
||||
bool forSecondApplication) const {
|
||||
auto *decl = getDeclOrNull();
|
||||
if (!decl || !decl->isImplicitlyUnwrappedOptional())
|
||||
return None;
|
||||
|
||||
auto *decl = getDecl();
|
||||
if (!decl->isImplicitlyUnwrappedOptional())
|
||||
return false;
|
||||
// If this isn't an IUO return () -> T!, it's an IUO value.
|
||||
if (!decl->getInterfaceType()->is<AnyFunctionType>())
|
||||
return IUOReferenceKind::Value;
|
||||
|
||||
auto itfType = decl->getInterfaceType();
|
||||
if (!itfType->getAs<AnyFunctionType>())
|
||||
return true;
|
||||
auto refKind = getFunctionRefKind();
|
||||
assert(!forSecondApplication || refKind == FunctionRefKind::DoubleApply);
|
||||
|
||||
switch (getFunctionRefKind()) {
|
||||
switch (refKind) {
|
||||
case FunctionRefKind::Unapplied:
|
||||
case FunctionRefKind::Compound:
|
||||
return false;
|
||||
// Such references never produce IUOs.
|
||||
return None;
|
||||
case FunctionRefKind::SingleApply:
|
||||
case FunctionRefKind::DoubleApply:
|
||||
return true;
|
||||
case FunctionRefKind::DoubleApply: {
|
||||
// Check whether this is a curried function reference e.g
|
||||
// (Self) -> (Args...) -> Ret. Such a function reference can only produce
|
||||
// an IUO on the second application.
|
||||
auto isCurried = decl->hasCurriedSelf() && !hasAppliedSelf(cs, *this);
|
||||
if (forSecondApplication != isCurried)
|
||||
return None;
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("unhandled kind");
|
||||
}
|
||||
return IUOReferenceKind::ReturnValue;
|
||||
}
|
||||
|
||||
SolutionResult ConstraintSystem::salvage() {
|
||||
|
||||
@@ -230,15 +230,6 @@ let _: Int = r
|
||||
class C<T> {}
|
||||
var sub: C! = C<Int>()
|
||||
|
||||
// FIXME: We probably shouldn't support this, we don't support other
|
||||
// 'direct call' features such as default arguments for curried calls.
|
||||
struct CurriedIUO {
|
||||
func silly() -> Int! { nil }
|
||||
func testSilly() {
|
||||
let _: Int = CurriedIUO.silly(self)()
|
||||
}
|
||||
}
|
||||
|
||||
// SR-15219 (rdar://83352038): Make sure we don't crash if an IUO param becomes
|
||||
// a placeholder.
|
||||
func rdar83352038() {
|
||||
@@ -251,3 +242,14 @@ func rdar83352038() {
|
||||
// Make sure we reject an attempt at a function conversion.
|
||||
func returnsIUO() -> Int! { 0 }
|
||||
let _ = (returnsIUO as () -> Int)() // expected-error {{cannot convert value of type '() -> Int?' to type '() -> Int' in coercion}}
|
||||
|
||||
// Make sure we only permit an IUO unwrap on the first application.
|
||||
func returnsIUOFn() -> (() -> Int?)! { nil }
|
||||
let _: (() -> Int?)? = returnsIUOFn()
|
||||
let _: (() -> Int)? = returnsIUOFn() // expected-error {{cannot convert value of type '(() -> Int?)?' to specified type '(() -> Int)?'}}
|
||||
let _: () -> Int? = returnsIUOFn()
|
||||
let _: () -> Int = returnsIUOFn() // expected-error {{cannot convert value of type '(() -> Int?)?' to specified type '() -> Int'}}
|
||||
let _: Int? = returnsIUOFn()()
|
||||
let _: Int = returnsIUOFn()() // expected-error {{value of optional type 'Int?' must be unwrapped to a value of type 'Int'}}
|
||||
// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
|
||||
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
|
||||
|
||||
@@ -25,7 +25,7 @@ func iuo_error(prop: IUOProperty) {
|
||||
let _: Coat? = prop.iuo!.optional!()
|
||||
let _: Coat? = prop.iuo!.optional!()!
|
||||
let _: Coat = prop.iuo.optional()
|
||||
// expected-error@-1 {{value of optional type '(() -> Coat)?' must be unwrapped}}
|
||||
// expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped}}
|
||||
// expected-note@-2{{coalesce}}
|
||||
// expected-note@-3{{force-unwrap}}
|
||||
let _: Coat = prop.iuo.optional()!
|
||||
@@ -35,7 +35,7 @@ func iuo_error(prop: IUOProperty) {
|
||||
let _: Coat = prop.iuo.optional!()
|
||||
let _: Coat = prop.iuo.optional!()!
|
||||
let _: Coat = prop.iuo!.optional()
|
||||
// expected-error@-1 {{value of optional type '(() -> Coat)?' must be unwrapped}}
|
||||
// expected-error@-1 {{value of optional type '(() -> Coat?)?' must be unwrapped}}
|
||||
// expected-note@-2{{coalesce}}
|
||||
// expected-note@-3{{force-unwrap}}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
// RUN: %target-swift-emit-silgen -module-name implicitly_unwrapped_optional %s | %FileCheck %s
|
||||
// RUN: %target-swift-emit-silgen -module-name implicitly_unwrapped_optional -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck %s
|
||||
|
||||
func foo(f f: (() -> ())!) {
|
||||
var f: (() -> ())! = f
|
||||
@@ -75,3 +75,44 @@ func sr3758() {
|
||||
let f: ((Any?) -> Void) = { (arg: Any!) in }
|
||||
f(nil)
|
||||
} // CHECK: end sil function '$s29implicitly_unwrapped_optional6sr3758yyF'
|
||||
|
||||
// SR-10492: Make sure we can SILGen all of the below without crashing:
|
||||
class SR_10492_C1 {
|
||||
init!() {}
|
||||
}
|
||||
|
||||
class SR_10492_C2 {
|
||||
init(_ foo: SR_10492_C1) {}
|
||||
}
|
||||
|
||||
@objc class C {
|
||||
@objc func foo() -> C! { nil }
|
||||
}
|
||||
|
||||
struct S {
|
||||
var i: Int!
|
||||
func foo() -> Int! { nil }
|
||||
subscript() -> Int! { 0 }
|
||||
|
||||
func testParend(_ anyObj: AnyObject) {
|
||||
let _: Int? = (foo)()
|
||||
let _: Int = (foo)()
|
||||
let _: Int? = foo.self()
|
||||
let _: Int = foo.self()
|
||||
let _: Int? = (self.foo.self)()
|
||||
let _: Int = (self.foo.self)()
|
||||
|
||||
// Not really paren'd, but a previous version of the compiler modeled it
|
||||
// that way.
|
||||
let _ = SR_10492_C2(SR_10492_C1())
|
||||
|
||||
let _: C = (anyObj.foo)!()
|
||||
}
|
||||
|
||||
func testCurried() {
|
||||
let _: Int? = S.foo(self)()
|
||||
let _: Int = S.foo(self)()
|
||||
let _: Int? = (S.foo(self).self)()
|
||||
let _: Int = (S.foo(self).self)()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user