Skip to content

Commit 50d83d7

Browse files
authored
Merge pull request #14246 from rudkx/rdar36913150-5.0
[5.0] Allow inout arguments that differ in optionality than the expected pa…
2 parents 0014166 + 0d1da95 commit 50d83d7

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)