Skip to content

Commit 649e0d9

Browse files
authored
Merge pull request swiftlang#14692 from rudkx/rdar37240984
[ConstraintSystem] Implicitly force results of @optional protocol req…
2 parents 749adec + 4ee6ead commit 649e0d9

File tree

4 files changed

+102
-10
lines changed

4 files changed

+102
-10
lines changed

lib/Sema/CSApply.cpp

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,8 @@ namespace {
10881088
// We also need to handle the implicitly unwrap of the result
10891089
// of the called function if that's the type checking solution
10901090
// we ended up with.
1091-
return forceUnwrapIfExpected(ref, member, memberLocator);
1091+
return forceUnwrapIfExpected(ref, member, memberLocator,
1092+
member->getAttrs().hasAttribute<OptionalAttr>());
10921093
}
10931094

10941095
// For types and properties, build member references.
@@ -2442,9 +2443,22 @@ namespace {
24422443
return expr;
24432444
}
24442445

2445-
Expr *forceUnwrapResult(Expr *expr) {
2446+
// Add a forced unwrap of an expression which either has type Optional<T>
2447+
// or is a function that returns an Optional<T>. The latter turns into a
2448+
// conversion expression that we will hoist above the ApplyExpr
2449+
// that needs to be forced during the process of rewriting the expression.
2450+
//
2451+
// forForcedOptional is used to indicate that we will further need
2452+
// to hoist this result above an explicit force of an optional that is
2453+
// in place for something like an @optional protocol member from
2454+
// Objective C that we might otherwise mistake for the thing we mean to
2455+
// force here.
2456+
Expr *forceUnwrapResult(Expr *expr, bool forForcedOptional =false) {
24462457
auto ty = simplifyType(cs.getType(expr));
24472458

2459+
if (forForcedOptional)
2460+
ty = ty->getOptionalObjectType();
2461+
24482462
if (auto *fnTy = ty->getAs<AnyFunctionType>()) {
24492463
auto underlyingType = cs.replaceFinalResultTypeWithUnderlying(fnTy);
24502464

@@ -2468,12 +2482,13 @@ namespace {
24682482
}
24692483

24702484
Expr *forceUnwrapIfExpected(Expr *expr, Decl *decl,
2471-
ConstraintLocatorBuilder locator) {
2485+
ConstraintLocatorBuilder locator,
2486+
bool forForcedOptional =false) {
24722487
if (!shouldForceUnwrapResult(decl, locator))
24732488
return expr;
24742489

24752490
// Force the expression if required for the solution.
2476-
return forceUnwrapResult(expr);
2491+
return forceUnwrapResult(expr, forForcedOptional);
24772492
}
24782493

24792494
Expr *visitDeclRefExpr(DeclRefExpr *expr) {
@@ -3851,9 +3866,29 @@ namespace {
38513866
}
38523867

38533868
Expr *visitForceValueExpr(ForceValueExpr *expr) {
3869+
// Check to see if we are forcing an
3870+
// ImplicitlyUnwrappedFunctionConversionExpr. This can happen
3871+
// in cases where we had a ForceValueExpr of an optional for a
3872+
// declaration for a function whose result type we need to
3873+
// implicitly force after applying. We need to hoist the function
3874+
// conversion above the ForceValueExpr, so that we may ultimately
3875+
// hoist it above the ApplyExpr where we will eventually rewrite the
3876+
// function conversion into a force of the result.
3877+
Expr *replacement = expr;
3878+
if (auto fnConv =
3879+
dyn_cast<ImplicitlyUnwrappedFunctionConversionExpr>(expr->getSubExpr())) {
3880+
auto fnConvSubExpr = fnConv->getSubExpr();
3881+
auto fnConvSubObjTy =
3882+
cs.getType(fnConvSubExpr)->getOptionalObjectType();
3883+
cs.setType(expr, fnConvSubObjTy);
3884+
expr->setSubExpr(fnConvSubExpr);
3885+
fnConv->setSubExpr(expr);
3886+
replacement = fnConv;
3887+
}
3888+
38543889
Type valueType = simplifyType(cs.getType(expr));
38553890
cs.setType(expr, valueType);
3856-
3891+
38573892
// Coerce the object type, if necessary.
38583893
auto subExpr = expr->getSubExpr();
38593894
if (auto objectTy = cs.getType(subExpr)->getOptionalObjectType()) {
@@ -3866,7 +3901,7 @@ namespace {
38663901
}
38673902
}
38683903

3869-
return expr;
3904+
return replacement;
38703905
}
38713906

38723907
Expr *visitOpenExistentialExpr(OpenExistentialExpr *expr) {

lib/Sema/ConstraintSystem.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,7 +1565,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
15651565
Type openedFullType;
15661566

15671567
bool isDynamicResult = choice.getKind() == OverloadChoiceKind::DeclViaDynamic;
1568-
bool createdDynamicResultDisjunction = false;
1568+
bool bindConstraintCreated = false;
15691569

15701570
switch (auto kind = choice.getKind()) {
15711571
case OverloadChoiceKind::Decl:
@@ -1623,6 +1623,19 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
16231623
//
16241624
// Subscript declarations are handled within
16251625
// getTypeOfMemberReference(); their result types are optional.
1626+
1627+
// Deal with values declared as implicitly unwrapped, or
1628+
// functions with return types that are implicitly unwrapped.
1629+
if (choice.isImplicitlyUnwrappedValueOrReturnValue()) {
1630+
// Build the disjunction to attempt binding both T? and T (or
1631+
// function returning T? and function returning T).
1632+
Type ty = createTypeVariable(locator, TVO_CanBindToInOut);
1633+
buildDisjunctionForImplicitlyUnwrappedOptional(ty, refType,
1634+
locator);
1635+
addConstraint(ConstraintKind::Bind, boundType,
1636+
OptionalType::get(ty->getRValueType()), locator);
1637+
bindConstraintCreated = true;
1638+
}
16261639
refType = OptionalType::get(refType->getRValueType());
16271640
}
16281641
// For a non-subscript declaration found via dynamic lookup, strip
@@ -1697,7 +1710,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
16971710
refType = OptionalType::get(refType->getRValueType());
16981711
}
16991712

1700-
createdDynamicResultDisjunction = true;
1713+
bindConstraintCreated = true;
17011714
}
17021715

17031716
// If the declaration is unavailable, note that in the score.
@@ -1813,8 +1826,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
18131826
openedFullType,
18141827
refType};
18151828

1816-
// We created appropriate disjunctions for dynamic result above.
1817-
if (!createdDynamicResultDisjunction) {
1829+
// In some cases we already created the appropriate bind constraints.
1830+
if (!bindConstraintCreated) {
18181831
if (choice.isImplicitlyUnwrappedValueOrReturnValue()) {
18191832
// Build the disjunction to attempt binding both T? and T (or
18201833
// function returning T? and function returning T).

test/Constraints/iuo_objc.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s
2+
// REQUIRES: objc_interop
3+
4+
import Foundation
5+
6+
func iuo_error(prop: IUOProperty) {
7+
let _: Coat? = prop.iuo.optional()
8+
// expected-error@-1 {{value of optional type '(() -> Coat?)?' not unwrapped; did you mean to use '!' or '?'?}}
9+
let _: Coat? = prop.iuo.optional()!
10+
// expected-error@-1 {{cannot invoke 'optional' with no arguments}}
11+
let _: Coat? = prop.iuo.optional!()
12+
let _: Coat? = prop.iuo.optional!()!
13+
let _: Coat? = prop.iuo!.optional()
14+
// expected-error@-1 {{value of optional type '(() -> Coat?)?' not unwrapped; did you mean to use '!' or '?'?}}
15+
let _: Coat? = prop.iuo!.optional()!
16+
// expected-error@-1 {{cannot invoke 'optional' with no arguments}}
17+
let _: Coat? = prop.iuo!.optional!()
18+
let _: Coat? = prop.iuo!.optional!()!
19+
let _: Coat = prop.iuo.optional()
20+
// expected-error@-1 {{value of optional type '(() -> Coat)?' not unwrapped; did you mean to use '!' or '?'?}}
21+
let _: Coat = prop.iuo.optional()!
22+
// expected-error@-1 {{cannot invoke 'optional' with no arguments}}
23+
let _: Coat = prop.iuo.optional!()
24+
let _: Coat = prop.iuo.optional!()!
25+
let _: Coat = prop.iuo!.optional()
26+
// expected-error@-1 {{value of optional type '(() -> Coat)?' not unwrapped; did you mean to use '!' or '?'?}}
27+
let _: Coat = prop.iuo!.optional()!
28+
// expected-error@-1 {{cannot invoke 'optional' with no arguments}}
29+
let _: Coat = prop.iuo!.optional!()
30+
let _: Coat = prop.iuo!.optional!()!
31+
}

test/Inputs/clang-importer-sdk/usr/include/Foundation.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,7 @@ typedef struct NonNilableReferences {
868868
@protocol NSProtocolWithOptionalRequirement
869869
@optional
870870
-(void)optionalRequirement;
871+
-(DummyClass *)optionalRequirementMethodWithIUOResult;
871872
@end
872873

873874
@interface NSClassWithMethodFromNSProtocolWithOptionalRequirement
@@ -1144,3 +1145,15 @@ void install_global_event_handler(_Nullable event_handler handler);
11441145

11451146
__nullable id returnNullableId(void);
11461147
void takeNullableId(__nullable id);
1148+
1149+
@interface I
1150+
@end
1151+
1152+
@protocol OptionalMethods
1153+
@optional
1154+
- (Coat *)optional;
1155+
@end
1156+
1157+
@interface IUOProperty
1158+
@property (readonly) id<OptionalMethods> iuo;
1159+
@end

0 commit comments

Comments
 (0)