Skip to content

Commit 0d1da95

Browse files
committed
Allow inout arguments that differ in optionality than the expected parameter.
Allow passing Optional<T> as inout where ImplicitlyUnwrappedOptional<T> is expected, and vice-versa. Swift 4.1 added a warning that overloading inouts by kind of optional was deprecated and would be removed, but we didn't actually allow people to remove an overload and pass arguments of the other kind of optional to the remaining function. Fixes rdar://problem/36913150 (cherry picked from commit 8685ee0)
1 parent 0014166 commit 0d1da95

File tree

6 files changed

+106
-14
lines changed

6 files changed

+106
-14
lines changed

lib/AST/ASTVerifier.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,8 +1069,17 @@ class Verifier : public ASTWalker {
10691069
Type srcObj = checkLValue(E->getSubExpr()->getType(),
10701070
"result of InOutExpr");
10711071
auto DestTy = E->getType()->castTo<InOutType>()->getObjectType();
1072-
1073-
checkSameType(DestTy, srcObj, "object types for InOutExpr");
1072+
1073+
// HACK: Allow differences in optionality of the source and
1074+
// result types. When IUO is gone from the type system we'll no
1075+
// longer need this.
1076+
auto srcOptObjTy = srcObj->getAnyOptionalObjectType();
1077+
auto dstOptObjTy = DestTy->getAnyOptionalObjectType();
1078+
if (srcOptObjTy && dstOptObjTy) {
1079+
checkSameType(srcOptObjTy, dstOptObjTy, "object types for InOutExpr");
1080+
} else {
1081+
checkSameType(DestTy, srcObj, "object types for InOutExpr");
1082+
}
10741083
verifyCheckedBase(E);
10751084
}
10761085

lib/Sema/CSApply.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5447,6 +5447,38 @@ static unsigned computeCallLevel(ConstraintSystem &cs, ConcreteDeclRef callee,
54475447
return 0;
54485448
}
54495449

5450+
// HACK: Support calling functions with inouts of differing kinds of
5451+
// optionality so that we can warn about overloading by IUO vs. plain
5452+
// Optional and allow users to correct the issue by removing an
5453+
// overload and passing the differently-optional value to the
5454+
// remaining overload.
5455+
bool inOutOptionalityDifferenceHack(Expr *arg, Type paramType,
5456+
ConstraintSystem &cs) {
5457+
auto *inOutArgTy = cs.getType(arg)->getAs<InOutType>();
5458+
if (!inOutArgTy)
5459+
return false;
5460+
5461+
auto *inOutParamTy = paramType->getAs<InOutType>();
5462+
if (!inOutParamTy)
5463+
return false;
5464+
5465+
OptionalTypeKind argOTK;
5466+
OptionalTypeKind paramOTK;
5467+
auto argObjTy = inOutArgTy->getObjectType()->getAnyOptionalObjectType(argOTK);
5468+
auto paramObjTy =
5469+
inOutParamTy->getObjectType()->getAnyOptionalObjectType(paramOTK);
5470+
5471+
if (argOTK == paramOTK || argOTK == OTK_None || paramOTK == OTK_None)
5472+
return false;
5473+
5474+
if (!argObjTy->isEqual(paramObjTy))
5475+
return false;
5476+
5477+
// Hammer over the argument type with the expected parameter type.
5478+
cs.setType(arg, paramType);
5479+
return true;
5480+
}
5481+
54505482
Expr *ExprRewriter::coerceCallArguments(
54515483
Expr *arg, AnyFunctionType *funcType,
54525484
ApplyExpr *apply,
@@ -5677,11 +5709,16 @@ Expr *ExprRewriter::coerceCallArguments(
56775709
continue;
56785710
}
56795711

5680-
// Convert the argument.
5681-
auto convertedArg = coerceToType(arg, paramType,
5682-
getArgLocator(argIdx, paramIdx));
5683-
if (!convertedArg)
5684-
return nullptr;
5712+
Expr *convertedArg;
5713+
if (inOutOptionalityDifferenceHack(arg, paramType, cs)) {
5714+
convertedArg = arg;
5715+
} else {
5716+
// Convert the argument.
5717+
convertedArg =
5718+
coerceToType(arg, paramType, getArgLocator(argIdx, paramIdx));
5719+
if (!convertedArg)
5720+
return nullptr;
5721+
}
56855722

56865723
// Add the converted argument.
56875724
fromTupleExpr[argIdx] = convertedArg;

lib/Sema/CSRanking.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) {
8080
case SK_ValueToPointerConversion:
8181
log << "value-to-pointer conversion";
8282
break;
83+
case SK_InOutOptionalityConversion:
84+
log << "inout optionality conversion";
8385
}
8486
log << ")\n";
8587
}

lib/Sema/CSSimplify.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,16 +1906,32 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
19061906
locator.withPathElement(
19071907
ConstraintLocator::ArrayElementType));
19081908

1909-
case TypeKind::InOut:
1909+
case TypeKind::InOut: {
19101910
// If the RHS is an inout type, the LHS must be an @lvalue type.
19111911
if (kind == ConstraintKind::BindParam ||
19121912
kind >= ConstraintKind::OperatorArgumentConversion)
19131913
return SolutionKind::Error;
1914-
1915-
return matchTypes(cast<InOutType>(desugar1)->getObjectType(),
1916-
cast<InOutType>(desugar2)->getObjectType(),
1917-
ConstraintKind::Equal, subflags,
1918-
locator.withPathElement(ConstraintLocator::ArrayElementType));
1914+
1915+
auto inoutObjTy1 = cast<InOutType>(desugar1)->getObjectType();
1916+
auto inoutObjTy2 = cast<InOutType>(desugar2)->getObjectType();
1917+
1918+
OptionalTypeKind OTK1;
1919+
OptionalTypeKind OTK2;
1920+
auto optionalObjTy1 = inoutObjTy1->getAnyOptionalObjectType(OTK1);
1921+
auto optionalObjTy2 = inoutObjTy2->getAnyOptionalObjectType(OTK2);
1922+
if (OTK1 != OTK2 && optionalObjTy1 && optionalObjTy2) {
1923+
increaseScore(ScoreKind::SK_InOutOptionalityConversion);
1924+
return matchTypes(inoutObjTy1,
1925+
inoutObjTy2,
1926+
ConstraintKind::ArgumentConversion, subflags,
1927+
locator.withPathElement(ConstraintLocator::ArrayElementType));
1928+
} else {
1929+
return matchTypes(inoutObjTy1,
1930+
inoutObjTy2,
1931+
ConstraintKind::Equal, subflags,
1932+
locator.withPathElement(ConstraintLocator::ArrayElementType));
1933+
}
1934+
}
19191935

19201936
case TypeKind::UnboundGeneric:
19211937
llvm_unreachable("Unbound generic type should have been opened");

lib/Sema/ConstraintSystem.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,12 @@ enum ScoreKind {
462462
SK_KeyPathSubscript,
463463
/// A conversion from a string, array, or inout to a pointer.
464464
SK_ValueToPointerConversion,
465+
/// A conversion from 'inout Optional<T>' to 'inout
466+
/// ImplicitlyUnwrappedOptional<T>' or vice-versa.
467+
/// FIXME: This goes away when IUO-as-a-type goes away.
468+
SK_InOutOptionalityConversion,
465469

466-
SK_LastScoreKind = SK_ValueToPointerConversion,
470+
SK_LastScoreKind = SK_InOutOptionalityConversion,
467471
};
468472

469473
/// The number of score kinds.

test/Constraints/iuo.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,27 @@ func cast<T : P>(_ t: T) {
159159
let _: (Bool) -> T? = id(T.iuoResultStatic as (Bool) -> T?)
160160
let _: T! = id(T.iuoResultStatic(true))
161161
}
162+
163+
func takesInOutIUO(_ i: inout Int!) {}
164+
func takesInOutOpt(_ o: inout Int?) {}
165+
166+
func overloadedByOptionality(_ a: inout Int!) {}
167+
// expected-note@-1 {{'overloadedByOptionality' previously declared here}}
168+
// expected-note@-2 {{'overloadedByOptionality' previously declared here}}
169+
func overloadedByOptionality(_ a: inout Int?) {}
170+
// expected-warning@-1 {{invalid redeclaration of 'overloadedByOptionality' which differs only by the kind of optional passed as an inout argument ('Int?' vs. 'Int!')}}
171+
// expected-warning@-2 {{invalid redeclaration of 'overloadedByOptionality' which differs only by the kind of optional passed as an inout argument ('Int?' vs. 'Int!')}}
172+
// expected-note@-3 {{overloading by kind of optional is deprecated and will be removed in a future release}}
173+
// expected-note@-4 {{overloading by kind of optional is deprecated and will be removed in a future release}}
174+
175+
func testInOutOptionality() {
176+
var i: Int! = 1
177+
var o: Int? = 2
178+
179+
takesInOutIUO(&i)
180+
takesInOutOpt(&i)
181+
takesInOutIUO(&o)
182+
takesInOutOpt(&o)
183+
184+
overloadedByOptionality(&o)
185+
}

0 commit comments

Comments
 (0)