Skip to content

Commit 5523ca7

Browse files
committed
[ConstraintSystem] Strip labels from enum elements when passed as values
Enum elements have to be treated the same way as regular functions when passed as values, which means labels have to be stripped from their argument types. Resolves: rdar://problem/32300339.
1 parent 2469674 commit 5523ca7

File tree

3 files changed

+210
-8
lines changed

3 files changed

+210
-8
lines changed

lib/Sema/ConstraintSystem.cpp

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -712,30 +712,53 @@ void ConstraintSystem::recordOpenedTypes(
712712

713713
/// Determine how many levels of argument labels should be removed from the
714714
/// function type when referencing the given declaration.
715-
static unsigned getNumRemovedArgumentLabels(ASTContext &ctx, ValueDecl *decl,
715+
static unsigned getNumRemovedArgumentLabels(TypeChecker &TC, ValueDecl *decl,
716716
bool isCurriedInstanceReference,
717717
FunctionRefKind functionRefKind) {
718+
unsigned numParameterLists = 0;
719+
720+
// Enum element with associated value has to be treated
721+
// as regular function value and all of the labels have to be
722+
// stripped from its parameters.
723+
//
724+
// enum E {
725+
// case foo(a: Int)
726+
// }
727+
// let bar: [Int] = []
728+
// bar.map(E.foo)
729+
//
730+
// `E.foo` has to act as a regular function type passed as a value.
731+
if (!TC.getLangOpts().isSwiftVersion3()) {
732+
if (auto *EED = dyn_cast<EnumElementDecl>(decl)) {
733+
numParameterLists = EED->hasAssociatedValues() ? 2 : 1;
734+
}
735+
}
736+
718737
// Only applicable to functions. Nothing else should have argument labels in
719738
// the type.
720-
auto func = dyn_cast<AbstractFunctionDecl>(decl);
721-
if (!func) return 0;
739+
if (auto func = dyn_cast<AbstractFunctionDecl>(decl))
740+
numParameterLists = func->getNumParameterLists();
741+
742+
if (numParameterLists == 0)
743+
return 0;
722744

723745
switch (functionRefKind) {
724746
case FunctionRefKind::Unapplied:
725747
case FunctionRefKind::Compound:
726748
// Always remove argument labels from unapplied references and references
727749
// that use a compound name.
728-
return func->getNumParameterLists();
750+
return numParameterLists;
729751

730752
case FunctionRefKind::SingleApply:
731753
// If we have fewer than two parameter lists, leave the labels.
732-
if (func->getNumParameterLists() < 2) return 0;
754+
if (numParameterLists < 2)
755+
return 0;
733756

734757
// If this is a curried reference to an instance method, where 'self' is
735758
// being applied, e.g., "ClassName.instanceMethod(self)", remove the
736759
// argument labels from the resulting function type. The 'self' parameter is
737760
// always unlabeled, so this operation is a no-op for the actual application.
738-
return isCurriedInstanceReference ? func->getNumParameterLists() : 1;
761+
return isCurriedInstanceReference ? numParameterLists : 1;
739762

740763
case FunctionRefKind::DoubleApply:
741764
// Never remove argument labels from a double application.
@@ -796,7 +819,7 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
796819
auto openedType =
797820
openFunctionType(
798821
funcType,
799-
getNumRemovedArgumentLabels(TC.Context, funcDecl,
822+
getNumRemovedArgumentLabels(TC, funcDecl,
800823
/*isCurriedInstanceReference=*/false,
801824
functionRefKind),
802825
locator, replacements,
@@ -1113,7 +1136,7 @@ ConstraintSystem::getTypeOfMemberReference(
11131136
auto &replacements = replacementsPtr ? *replacementsPtr : localReplacements;
11141137
bool isCurriedInstanceReference = value->isInstanceMember() && !isInstance;
11151138
unsigned numRemovedArgumentLabels =
1116-
getNumRemovedArgumentLabels(TC.Context, value, isCurriedInstanceReference,
1139+
getNumRemovedArgumentLabels(TC, value, isCurriedInstanceReference,
11171140
functionRefKind);
11181141

11191142
AnyFunctionType *funcType;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 3
2+
3+
// Tests for enum case behavior in Swift 3
4+
5+
enum E {
6+
case foo(bar: String)
7+
case bar(_: String)
8+
case two(x: Int, y: Int)
9+
case tuple((x: Int, y: Int))
10+
}
11+
12+
enum G_E<T> {
13+
case foo(bar: T)
14+
case bar(_: T)
15+
case two(x: T, y: T)
16+
case tuple((x: T, y: T))
17+
}
18+
19+
let arr: [String] = []
20+
let _ = arr.map(E.foo) // Ok
21+
let _ = arr.map(E.bar) // Ok
22+
let _ = arr.map(E.two) // expected-error {{cannot convert value of type '(Int, Int) -> E' to expected argument type '(String) -> _'}}
23+
let _ = arr.map(E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> E' to expected argument type '(String) -> _'}}
24+
25+
let _ = arr.map(G_E<String>.foo) // Ok
26+
let _ = arr.map(G_E<String>.bar) // Ok
27+
let _ = arr.map(G_E<String>.two) // expected-error {{cannot convert value of type '(String, String) -> G_E<String>' to expected argument type '(String) -> _'}}
28+
let _ = arr.map(G_E<Int>.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E<Int>' to expected argument type '(String) -> _'}}
29+
30+
let _ = E.foo("hello") // expected-error {{missing argument label 'bar:' in call}}
31+
let _ = E.bar("hello") // Ok
32+
33+
let _ = G_E<String>.foo("hello") // expected-error {{missing argument label 'bar:' in call}}
34+
let _ = G_E<String>.bar("hello") // Ok
35+
36+
// Passing enum case as an argument to generic function
37+
38+
func bar_1<T>(_: (T) -> E) {}
39+
func bar_2<T>(_: (T) -> G_E<T>) {}
40+
func bar_3<T, U>(_: (T) -> G_E<U>) {}
41+
42+
bar_1(E.foo) // Ok
43+
bar_1(E.bar) // Ok
44+
bar_1(E.two) // Ok in Swift 3
45+
bar_1(E.tuple) // Ok - it's going to be ((x: Int, y: Int))
46+
47+
bar_2(G_E<String>.foo) // Ok
48+
bar_2(G_E<Int>.bar) // Ok
49+
bar_2(G_E<Int>.two) // expected-error {{cannot convert value of type '(Int, Int) -> G_E<Int>' to expected argument type '(_) -> G_E<_>'}}
50+
bar_2(G_E<Int>.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E<Int>' to expected argument type '(_) -> G_E<_>'}}
51+
bar_3(G_E<Int>.tuple) // Ok
52+
53+
// Regular enum case assigned as a value
54+
55+
let foo: (String) -> E = E.foo // Ok
56+
let _ = foo("hello") // Ok
57+
58+
let bar: (String) -> E = E.bar // Ok
59+
let _ = bar("hello") // Ok
60+
61+
let two: (Int, Int) -> E = E.two // Ok
62+
let _ = two(0, 42) // Ok
63+
64+
let tuple: ((x: Int, y: Int)) -> E = E.tuple // Ok
65+
let _ = tuple((x: 0, y: 42)) // Ok
66+
67+
// Generic enum case assigned as a value
68+
69+
let g_foo: (String) -> G_E<String> = G_E<String>.foo // Ok
70+
let _ = g_foo("hello") // Ok
71+
72+
let g_bar: (String) -> G_E<String> = G_E<String>.bar // Ok
73+
let _ = g_bar("hello") // Ok
74+
75+
let g_two: (Int, Int) -> G_E<Int> = G_E<Int>.two // Ok
76+
let _ = g_two(0, 42) // Ok
77+
78+
let g_tuple: ((x: Int, y: Int)) -> G_E<Int> = G_E<Int>.tuple // Ok
79+
let _ = g_tuple((x: 0, y: 42)) // Ok
80+
81+
// Following example doesn't work in Swift 3 mode because Foo.a and Foo.b have different argument types
82+
// since the labels are not striped from them.
83+
84+
enum Foo {
85+
case a(x: Int)
86+
case b(y: Int)
87+
}
88+
89+
func foo<T>(_: T, _: T) {}
90+
foo(Foo.a, Foo.b) // expected-error {{cannot invoke 'foo' with an argument list of type '((Int) -> Foo, (Int) -> Foo)}}
91+
// expected-note@-1 {{expected an argument list of type '(T, T)'}}

test/Constraints/enum_cases.swift

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 4
2+
3+
// See test/Compatibility/enum_cases.swift for Swift 3 behavior
4+
5+
enum E {
6+
case foo(bar: String)
7+
case bar(_: String)
8+
case two(x: Int, y: Int)
9+
case tuple((x: Int, y: Int))
10+
}
11+
12+
enum G_E<T> {
13+
case foo(bar: T)
14+
case bar(_: T)
15+
case two(x: T, y: T)
16+
case tuple((x: T, y: T))
17+
}
18+
19+
let arr: [String] = []
20+
let _ = arr.map(E.foo) // Ok
21+
let _ = arr.map(E.bar) // Ok
22+
let _ = arr.map(E.two) // expected-error {{cannot convert value of type '(Int, Int) -> E' to expected argument type '(String) -> _'}}
23+
let _ = arr.map(E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> E' to expected argument type '(String) -> _'}}
24+
25+
let _ = arr.map(G_E<String>.foo) // Ok
26+
let _ = arr.map(G_E<String>.bar) // Ok
27+
let _ = arr.map(G_E<String>.two) // expected-error {{cannot convert value of type '(String, String) -> G_E<String>' to expected argument type '(String) -> _'}}
28+
let _ = arr.map(G_E<Int>.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E<Int>' to expected argument type '(String) -> _'}}
29+
30+
let _ = E.foo("hello") // expected-error {{missing argument label 'bar:' in call}}
31+
let _ = E.bar("hello") // Ok
32+
33+
let _ = G_E<String>.foo("hello") // expected-error {{missing argument label 'bar:' in call}}
34+
let _ = G_E<String>.bar("hello") // Ok
35+
36+
// Passing enum case as an argument to generic function
37+
38+
func bar_1<T>(_: (T) -> E) {}
39+
func bar_2<T>(_: (T) -> G_E<T>) {}
40+
func bar_3<T, U>(_: (T) -> G_E<U>) {}
41+
42+
bar_1(E.foo) // Ok
43+
bar_1(E.bar) // Ok
44+
bar_1(E.two) // expected-error {{cannot convert value of type '(Int, Int) -> E' to expected argument type '(_) -> E'}}
45+
bar_1(E.tuple) // Ok - it's going to be ((x: Int, y: Int))
46+
47+
bar_2(G_E<String>.foo) // Ok
48+
bar_2(G_E<Int>.bar) // Ok
49+
bar_2(G_E<Int>.two) // expected-error {{cannot convert value of type '(Int, Int) -> G_E<Int>' to expected argument type '(_) -> G_E<_>'}}
50+
bar_2(G_E<Int>.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E<Int>' to expected argument type '(_) -> G_E<_>'}}
51+
bar_3(G_E<Int>.tuple) // Ok
52+
53+
// Regular enum case assigned as a value
54+
55+
let foo: (String) -> E = E.foo // Ok
56+
let _ = foo("hello") // Ok
57+
58+
let bar: (String) -> E = E.bar // Ok
59+
let _ = bar("hello") // Ok
60+
61+
let two: (Int, Int) -> E = E.two // Ok
62+
let _ = two(0, 42) // Ok
63+
64+
let tuple: ((x: Int, y: Int)) -> E = E.tuple // Ok
65+
let _ = tuple((x: 0, y: 42)) // Ok
66+
67+
// Generic enum case assigned as a value
68+
69+
let g_foo: (String) -> G_E<String> = G_E<String>.foo // Ok
70+
let _ = g_foo("hello") // Ok
71+
72+
let g_bar: (String) -> G_E<String> = G_E<String>.bar // Ok
73+
let _ = g_bar("hello") // Ok
74+
75+
let g_two: (Int, Int) -> G_E<Int> = G_E<Int>.two // Ok
76+
let _ = g_two(0, 42) // Ok
77+
78+
let g_tuple: ((x: Int, y: Int)) -> G_E<Int> = G_E<Int>.tuple // Ok
79+
let _ = g_tuple((x: 0, y: 42)) // Ok
80+
81+
82+
enum Foo {
83+
case a(x: Int)
84+
case b(y: Int)
85+
}
86+
87+
func foo<T>(_: T, _: T) {}
88+
foo(Foo.a, Foo.b) // Ok in Swift 4 because we strip labels from the arguments

0 commit comments

Comments
 (0)