Skip to content

Commit 30c4235

Browse files
committed
Sema: Horrific simulation of Swift 3 bug with argument labels for Swift 3 mode
In Swift 3.0.1, argument labels are ignored when calling a function having a single parameter of 'Any' type. That is, if we have: func foo(_: Any) {} Both of the following were accepted in a no-assert build (an assert build would crash, but the GM builds of Xcode ship with asserts off): foo(123) foo(data: 123) This behavior was fixed by 578e36a, but unfortunately we have to revert to the old behavior *and* defeat the assertion when in Swift 3 mode. Swift 4 mode still has the correct behavior, where the second call 'foo(data: 123)' produces a diagnostic. Now, I have to pour myself a strong drink to forget this ever happened. Fixes <rdar://problem/28952837>.
1 parent a1b113f commit 30c4235

File tree

6 files changed

+83
-23
lines changed

6 files changed

+83
-23
lines changed

lib/Sema/CSApply.cpp

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4702,13 +4702,34 @@ Expr *ExprRewriter::coerceCallArguments(
47024702
bool hasTrailingClosure,
47034703
ConstraintLocatorBuilder locator) {
47044704

4705-
// Total hack: In Swift 3 mode, we can end up with an arity mismatch due to
4706-
// loss of ParenType sugar.
4705+
// Local function to produce a locator to refer to the ith element of the
4706+
// argument tuple.
4707+
auto getArgLocator = [&](unsigned argIdx, unsigned paramIdx)
4708+
-> ConstraintLocatorBuilder {
4709+
return locator.withPathElement(
4710+
LocatorPathElt::getApplyArgToParam(argIdx, paramIdx));
4711+
};
4712+
4713+
bool matchCanFail = false;
4714+
4715+
// If you value your sanity, ignore the body of this 'if' statement.
47074716
if (cs.getASTContext().isSwiftVersion3()) {
4717+
// Total hack: In Swift 3 mode, we can end up with an arity mismatch due to
4718+
// loss of ParenType sugar.
47084719
if (isa<TupleExpr>(arg))
47094720
if (auto *parenType = dyn_cast<ParenType>(paramType.getPointer()))
47104721
if (isa<TupleType>(parenType->getUnderlyingType().getPointer()))
47114722
paramType = parenType->getUnderlyingType();
4723+
4724+
// Total hack: In Swift 3 mode, argument labels are ignored when calling
4725+
// function type with a single Any parameter.
4726+
if (paramType->isAny()) {
4727+
if (auto tupleArgType = dyn_cast<TupleType>(arg->getType().getPointer())) {
4728+
if (tupleArgType->getNumElements() == 1) {
4729+
matchCanFail = true;
4730+
}
4731+
}
4732+
}
47124733
}
47134734

47144735
bool allParamsMatch = cs.getType(arg)->isEqual(paramType);
@@ -4778,7 +4799,8 @@ Expr *ExprRewriter::coerceCallArguments(
47784799
hasTrailingClosure,
47794800
/*allowFixes=*/false, listener,
47804801
parameterBindings);
4781-
assert(!failed && "Call arguments did not match up?");
4802+
4803+
assert((matchCanFail || !failed) && "Call arguments did not match up?");
47824804
(void)failed;
47834805

47844806
// We should either have parentheses or a tuple.
@@ -4810,14 +4832,6 @@ Expr *ExprRewriter::coerceCallArguments(
48104832
return Identifier();
48114833
};
48124834

4813-
// Local function to produce a locator to refer to the ith element of the
4814-
// argument tuple.
4815-
auto getArgLocator = [&](unsigned argIdx, unsigned paramIdx)
4816-
-> ConstraintLocatorBuilder {
4817-
return locator.withPathElement(
4818-
LocatorPathElt::getApplyArgToParam(argIdx, paramIdx));
4819-
};
4820-
48214835
auto &tc = getConstraintSystem().getTypeChecker();
48224836
SmallVector<TupleTypeElt, 4> toSugarFields;
48234837
SmallVector<TupleTypeElt, 4> fromTupleExprFields(

lib/Sema/CSSimplify.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,26 @@ static ConstraintSystem::SolutionKind
623623
matchCallArguments(ConstraintSystem &cs, ConstraintKind kind,
624624
Type argType, Type paramType,
625625
ConstraintLocatorBuilder locator) {
626+
627+
if (paramType->isAny()) {
628+
if (argType->is<InOutType>())
629+
return ConstraintSystem::SolutionKind::Error;
630+
631+
// If the param type is Any, the function can only have one argument.
632+
// Check if exactly one argument was passed to this function, otherwise
633+
// we obviously have a mismatch.
634+
if (auto tupleArgType = dyn_cast<TupleType>(argType.getPointer())) {
635+
// Total hack: In Swift 3 mode, argument labels are ignored when calling
636+
// function type with a single Any parameter.
637+
if (tupleArgType->getNumElements() != 1 ||
638+
(!cs.getASTContext().isSwiftVersion3() &&
639+
tupleArgType->getElement(0).hasName())) {
640+
return ConstraintSystem::SolutionKind::Error;
641+
}
642+
}
643+
return ConstraintSystem::SolutionKind::Solved;
644+
}
645+
626646
// Extract the parameters.
627647
ValueDecl *callee;
628648
unsigned calleeLevel;
@@ -644,16 +664,6 @@ matchCallArguments(ConstraintSystem &cs, ConstraintKind kind,
644664
parameterBindings))
645665
return ConstraintSystem::SolutionKind::Error;
646666

647-
// In the empty existential parameter case,
648-
// it's sufficient to simply match call arguments.
649-
if (paramType->isAny()) {
650-
// Argument of the existential type can't be inout.
651-
if (argType->is<InOutType>())
652-
return ConstraintSystem::SolutionKind::Error;
653-
654-
return ConstraintSystem::SolutionKind::Solved;
655-
}
656-
657667
// Check the argument types for each of the parameters.
658668
ConstraintSystem::TypeMatchOptions subflags =
659669
ConstraintSystem::TMF_GenerateConstraints;

test/Compatibility/tuple_arguments.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,3 +1269,17 @@ do {
12691269
let _: (Int, Int) -> () = { _ = ($0, $1) }
12701270
let _: (Int, Int) -> () = { t, u in _ = (t, u) }
12711271
}
1272+
1273+
// rdar://problem/28952837 - argument labels ignored when calling function
1274+
// with single 'Any' parameter
1275+
func takesAny(_: Any) {}
1276+
1277+
do {
1278+
let fn: (Any) -> () = { _ in }
1279+
1280+
fn(123)
1281+
fn(data: 123)
1282+
1283+
takesAny(123)
1284+
takesAny(data: 123)
1285+
}

test/Constraints/closures.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,9 @@ class C_SR_2505 : P_SR_2505 {
349349
}
350350

351351
func call(_ c: C_SR_2505) -> Bool {
352+
// Note: no diagnostic about capturing 'self', because this is a
353+
// non-escaping closure -- that's how we know we have selected
354+
// test(it:) and not test(_)
352355
return c.test { o in test(o) }
353356
}
354357
}

test/Constraints/diagnostics.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -829,9 +829,13 @@ func foo1255_2() -> Int {
829829

830830
// SR-2505: "Call arguments did not match up" assertion
831831

832+
// Here we're simulating the busted Swift 3 behavior -- see
833+
// test/Constraints/diagnostics_swift4.swift for the correct
834+
// behavior.
835+
832836
func sr_2505(_ a: Any) {} // expected-note {{}}
833837
sr_2505() // expected-error {{missing argument for parameter #1 in call}}
834-
sr_2505(a: 1) // expected-error {{extraneous argument label 'a:' in call}}
838+
sr_2505(a: 1) // FIXME: emit a warning saying this becomes an error in Swift 4
835839
sr_2505(1, 2) // expected-error {{extra argument in call}}
836840
sr_2505(a: 1, 2) // expected-error {{extra argument in call}}
837841

@@ -851,7 +855,8 @@ extension C_2505 {
851855
class C2_2505: P_2505 {
852856
}
853857

854-
let c_2505 = C_2505(arg: [C2_2505()]) // expected-error {{argument labels '(arg:)' do not match any available overloads}} expected-note {{overloads for 'C_2505' exist}}
858+
// FIXME: emit a warning saying this becomes an error in Swift 4
859+
let c_2505 = C_2505(arg: [C2_2505()])
855860

856861
// Diagnostic message for initialization with binary operations as right side
857862
let foo1255_3: String = 1 + 2 + 3 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}

test/Constraints/tuple_arguments.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,3 +1254,17 @@ do {
12541254
let _: (Int, Int) -> () = { _ = ($0, $1) }
12551255
let _: (Int, Int) -> () = { t, u in _ = (t, u) }
12561256
}
1257+
1258+
// rdar://problem/28952837 - argument labels ignored when calling function
1259+
// with single 'Any' parameter
1260+
func takesAny(_: Any) {}
1261+
1262+
do {
1263+
let fn: (Any) -> () = { _ in }
1264+
1265+
fn(123)
1266+
fn(data: 123) // expected-error {{extraneous argument label 'data:' in call}}
1267+
1268+
takesAny(123)
1269+
takesAny(data: 123) // expected-error {{extraneous argument label 'data:' in call}}
1270+
}

0 commit comments

Comments
 (0)