Permalink
Browse files

Sema: Horrific simulation of Swift 3 bug with argument labels for Swi…

…ft 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>.
  • Loading branch information...
slavapestov committed Dec 14, 2016
1 parent a1b113f commit 30c4235193b64050f8110ef5598c7efb4501e0da
View
@@ -4702,13 +4702,34 @@ Expr *ExprRewriter::coerceCallArguments(
bool hasTrailingClosure,
ConstraintLocatorBuilder locator) {
// Total hack: In Swift 3 mode, we can end up with an arity mismatch due to
// loss of ParenType sugar.
// Local function to produce a locator to refer to the ith element of the
// argument tuple.
auto getArgLocator = [&](unsigned argIdx, unsigned paramIdx)
-> ConstraintLocatorBuilder {
return locator.withPathElement(
LocatorPathElt::getApplyArgToParam(argIdx, paramIdx));
};
bool matchCanFail = false;
// If you value your sanity, ignore the body of this 'if' statement.
if (cs.getASTContext().isSwiftVersion3()) {
// Total hack: In Swift 3 mode, we can end up with an arity mismatch due to
// loss of ParenType sugar.
if (isa<TupleExpr>(arg))
if (auto *parenType = dyn_cast<ParenType>(paramType.getPointer()))
if (isa<TupleType>(parenType->getUnderlyingType().getPointer()))
paramType = parenType->getUnderlyingType();
// Total hack: In Swift 3 mode, argument labels are ignored when calling
// function type with a single Any parameter.
if (paramType->isAny()) {
if (auto tupleArgType = dyn_cast<TupleType>(arg->getType().getPointer())) {
if (tupleArgType->getNumElements() == 1) {
matchCanFail = true;
}
}
}
}
bool allParamsMatch = cs.getType(arg)->isEqual(paramType);
@@ -4778,7 +4799,8 @@ Expr *ExprRewriter::coerceCallArguments(
hasTrailingClosure,
/*allowFixes=*/false, listener,
parameterBindings);
assert(!failed && "Call arguments did not match up?");
assert((matchCanFail || !failed) && "Call arguments did not match up?");
(void)failed;
// We should either have parentheses or a tuple.
@@ -4810,14 +4832,6 @@ Expr *ExprRewriter::coerceCallArguments(
return Identifier();
};
// Local function to produce a locator to refer to the ith element of the
// argument tuple.
auto getArgLocator = [&](unsigned argIdx, unsigned paramIdx)
-> ConstraintLocatorBuilder {
return locator.withPathElement(
LocatorPathElt::getApplyArgToParam(argIdx, paramIdx));
};
auto &tc = getConstraintSystem().getTypeChecker();
SmallVector<TupleTypeElt, 4> toSugarFields;
SmallVector<TupleTypeElt, 4> fromTupleExprFields(
View
@@ -623,6 +623,26 @@ static ConstraintSystem::SolutionKind
matchCallArguments(ConstraintSystem &cs, ConstraintKind kind,
Type argType, Type paramType,
ConstraintLocatorBuilder locator) {
if (paramType->isAny()) {
if (argType->is<InOutType>())
return ConstraintSystem::SolutionKind::Error;
// If the param type is Any, the function can only have one argument.
// Check if exactly one argument was passed to this function, otherwise
// we obviously have a mismatch.
if (auto tupleArgType = dyn_cast<TupleType>(argType.getPointer())) {
// Total hack: In Swift 3 mode, argument labels are ignored when calling
// function type with a single Any parameter.
if (tupleArgType->getNumElements() != 1 ||
(!cs.getASTContext().isSwiftVersion3() &&
tupleArgType->getElement(0).hasName())) {
return ConstraintSystem::SolutionKind::Error;
}
}
return ConstraintSystem::SolutionKind::Solved;
}
// Extract the parameters.
ValueDecl *callee;
unsigned calleeLevel;
@@ -644,16 +664,6 @@ matchCallArguments(ConstraintSystem &cs, ConstraintKind kind,
parameterBindings))
return ConstraintSystem::SolutionKind::Error;
// In the empty existential parameter case,
// it's sufficient to simply match call arguments.
if (paramType->isAny()) {
// Argument of the existential type can't be inout.
if (argType->is<InOutType>())
return ConstraintSystem::SolutionKind::Error;
return ConstraintSystem::SolutionKind::Solved;
}
// Check the argument types for each of the parameters.
ConstraintSystem::TypeMatchOptions subflags =
ConstraintSystem::TMF_GenerateConstraints;
@@ -1269,3 +1269,17 @@ do {
let _: (Int, Int) -> () = { _ = ($0, $1) }
let _: (Int, Int) -> () = { t, u in _ = (t, u) }
}
// rdar://problem/28952837 - argument labels ignored when calling function
// with single 'Any' parameter
func takesAny(_: Any) {}
do {
let fn: (Any) -> () = { _ in }
fn(123)
fn(data: 123)
takesAny(123)
takesAny(data: 123)
}
@@ -349,6 +349,9 @@ class C_SR_2505 : P_SR_2505 {
}
func call(_ c: C_SR_2505) -> Bool {
// Note: no diagnostic about capturing 'self', because this is a
// non-escaping closure -- that's how we know we have selected
// test(it:) and not test(_)
return c.test { o in test(o) }
}
}
@@ -829,9 +829,13 @@ func foo1255_2() -> Int {
// SR-2505: "Call arguments did not match up" assertion
// Here we're simulating the busted Swift 3 behavior -- see
// test/Constraints/diagnostics_swift4.swift for the correct
// behavior.
func sr_2505(_ a: Any) {} // expected-note {{}}
sr_2505() // expected-error {{missing argument for parameter #1 in call}}
sr_2505(a: 1) // expected-error {{extraneous argument label 'a:' in call}}
sr_2505(a: 1) // FIXME: emit a warning saying this becomes an error in Swift 4
sr_2505(1, 2) // expected-error {{extra argument in call}}
sr_2505(a: 1, 2) // expected-error {{extra argument in call}}
@@ -851,7 +855,8 @@ extension C_2505 {
class C2_2505: P_2505 {
}
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}}
// FIXME: emit a warning saying this becomes an error in Swift 4
let c_2505 = C_2505(arg: [C2_2505()])
// Diagnostic message for initialization with binary operations as right side
let foo1255_3: String = 1 + 2 + 3 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
@@ -1254,3 +1254,17 @@ do {
let _: (Int, Int) -> () = { _ = ($0, $1) }
let _: (Int, Int) -> () = { t, u in _ = (t, u) }
}
// rdar://problem/28952837 - argument labels ignored when calling function
// with single 'Any' parameter
func takesAny(_: Any) {}
do {
let fn: (Any) -> () = { _ in }
fn(123)
fn(data: 123) // expected-error {{extraneous argument label 'data:' in call}}
takesAny(123)
takesAny(data: 123) // expected-error {{extraneous argument label 'data:' in call}}
}

0 comments on commit 30c4235

Please sign in to comment.