Skip to content

Commit 659f5df

Browse files
xedinrjmccall
authored andcommitted
[AST] PackExpansionMatcher: use common prefix/suffix algorithm for tuple matching
1 parent eb475b4 commit 659f5df

File tree

3 files changed

+54
-109
lines changed

3 files changed

+54
-109
lines changed

include/swift/AST/PackExpansionMatcher.h

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,6 @@ struct MatchedPair {
4141
: lhs(lhs), rhs(rhs), lhsIdx(lhsIdx), rhsIdx(rhsIdx) {}
4242
};
4343

44-
/// Performs a structural match of two lists of tuple elements. The invariant
45-
/// is that a pack expansion type must not be followed by an unlabeled
46-
/// element, that is, it is either the last element or the next element has
47-
/// a label.
48-
///
49-
/// In this manner, an element with a pack expansion type "absorbs" all
50-
/// unlabeled elements up to the next label. An element with any other type
51-
/// matches exactly one element on the other side.
52-
class TuplePackMatcher {
53-
ArrayRef<TupleTypeElt> lhsElts;
54-
ArrayRef<TupleTypeElt> rhsElts;
55-
56-
ASTContext &ctx;
57-
58-
public:
59-
SmallVector<MatchedPair, 4> pairs;
60-
61-
TuplePackMatcher(TupleType *lhsTuple, TupleType *rhsTuple);
62-
63-
bool match();
64-
};
65-
6644
/// Performs a structural match of two lists of types.
6745
///
6846
/// The invariant is that each list must only contain at most one pack
@@ -76,16 +54,18 @@ class TypeListPackMatcher {
7654
Type type;
7755
ParameterTypeFlags flags;
7856

79-
public:
8057
Element(Identifier label, Type type,
8158
ParameterTypeFlags flags = ParameterTypeFlags())
8259
: label(label), type(type), flags(flags) {}
8360

61+
public:
8462
bool hasLabel() const { return !label.empty(); }
8563
Identifier getLabel() const { return label; }
8664

8765
Type getType() const { return type; }
8866

67+
ParameterTypeFlags getFlags() const { return flags; }
68+
8969
static Element from(const TupleTypeElt &tupleElt);
9070
static Element from(const AnyFunctionType::Param &funcParam);
9171
static Element from(Type type);
@@ -108,7 +88,21 @@ class TypeListPackMatcher {
10888
public:
10989
SmallVector<MatchedPair, 4> pairs;
11090

111-
bool match();
91+
[[nodiscard]] bool match();
92+
};
93+
94+
/// Performs a structural match of two lists of tuple elements.
95+
///
96+
/// The invariant is that each list must only contain at most one pack
97+
/// expansion type. After collecting a common prefix and suffix, the
98+
/// pack expansion on either side asborbs the remaining elements on the
99+
/// other side.
100+
class TuplePackMatcher : public TypeListPackMatcher {
101+
public:
102+
TuplePackMatcher(TupleType *lhsTuple, TupleType *rhsTuple)
103+
: TypeListPackMatcher(lhsTuple->getASTContext(),
104+
lhsTuple->getElements(),
105+
rhsTuple->getElements()) {}
112106
};
113107

114108
/// Performs a structural match of two lists of (unlabeled) function

lib/AST/PackExpansionMatcher.cpp

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -39,90 +39,6 @@ static PackExpansionType *createPackBinding(ASTContext &ctx,
3939
return PackExpansionType::get(packType, packType);
4040
}
4141

42-
static PackExpansionType *gatherTupleElements(ArrayRef<TupleTypeElt> &elts,
43-
Identifier name,
44-
ASTContext &ctx) {
45-
SmallVector<Type, 2> types;
46-
47-
if (!elts.empty() && elts.front().getName() == name) {
48-
do {
49-
types.push_back(elts.front().getType());
50-
elts = elts.slice(1);
51-
} while (!elts.empty() && !elts.front().hasName());
52-
}
53-
54-
return createPackBinding(ctx, types);
55-
}
56-
57-
TuplePackMatcher::TuplePackMatcher(TupleType *lhsTuple, TupleType *rhsTuple)
58-
: lhsElts(lhsTuple->getElements()),
59-
rhsElts(rhsTuple->getElements()),
60-
ctx(lhsTuple->getASTContext()) {}
61-
62-
bool TuplePackMatcher::match() {
63-
unsigned lhsIdx = 0;
64-
unsigned rhsIdx = 0;
65-
66-
// Iterate over the two tuples in parallel, popping elements from
67-
// the start.
68-
while (true) {
69-
// If both tuples have been exhausted, we're done.
70-
if (lhsElts.empty() && rhsElts.empty())
71-
return false;
72-
73-
if (lhsElts.empty()) {
74-
assert(!rhsElts.empty());
75-
return true;
76-
}
77-
78-
// A pack expansion type on the left hand side absorbs all elements
79-
// from the right hand side up to the next mismatched label.
80-
auto lhsElt = lhsElts.front();
81-
if (auto *lhsExpansionType = lhsElt.getType()->getAs<PackExpansionType>()) {
82-
lhsElts = lhsElts.slice(1);
83-
84-
assert(lhsElts.empty() || lhsElts.front().hasName() &&
85-
"Tuple element with pack expansion type cannot be followed "
86-
"by an unlabeled element");
87-
88-
auto rhs = gatherTupleElements(rhsElts, lhsElt.getName(), ctx);
89-
pairs.emplace_back(lhsExpansionType, rhs, lhsIdx++, rhsIdx);
90-
continue;
91-
}
92-
93-
if (rhsElts.empty()) {
94-
assert(!lhsElts.empty());
95-
return true;
96-
}
97-
98-
// A pack expansion type on the right hand side absorbs all elements
99-
// from the left hand side up to the next mismatched label.
100-
auto rhsElt = rhsElts.front();
101-
if (auto *rhsExpansionType = rhsElt.getType()->getAs<PackExpansionType>()) {
102-
rhsElts = rhsElts.slice(1);
103-
104-
assert(rhsElts.empty() || rhsElts.front().hasName() &&
105-
"Tuple element with pack expansion type cannot be followed "
106-
"by an unlabeled element");
107-
108-
auto lhs = gatherTupleElements(lhsElts, rhsElt.getName(), ctx);
109-
pairs.emplace_back(lhs, rhsExpansionType, lhsIdx, rhsIdx++);
110-
continue;
111-
}
112-
113-
// Neither side is a pack expansion. We must have an exact match.
114-
if (lhsElt.getName() != rhsElt.getName())
115-
return true;
116-
117-
lhsElts = lhsElts.slice(1);
118-
rhsElts = rhsElts.slice(1);
119-
120-
pairs.emplace_back(lhsElt.getType(), rhsElt.getType(), lhsIdx++, rhsIdx++);
121-
}
122-
123-
return false;
124-
}
125-
12642
TypeListPackMatcher::Element
12743
TypeListPackMatcher::Element::from(const TupleTypeElt &elt) {
12844
return {elt.getName(), elt.getType()};

test/Constraints/pack-expansion-expressions.swift

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,19 @@ do {
172172
get { 42 }
173173
set {}
174174
}
175+
176+
subscript<each T>(simpleTuple args: (repeat each T)) -> Int {
177+
get { return 0 }
178+
set {}
179+
}
180+
181+
subscript<each T>(compoundTuple args: (String, repeat each T)) -> Int {
182+
get { return 0 }
183+
set {}
184+
}
175185
}
176186

177-
func test_that_variadic_generics_claim_unlabeled_arguments<each T>(_ args: repeat each T, test: inout TestArgMatching) {
187+
func test_that_variadic_generics_claim_unlabeled_arguments<each T>(_ args: repeat each T, test: inout TestArgMatching, extra: String) {
178188
func testLabeled<each U>(data: repeat each U) {}
179189
func testUnlabeled<each U>(_: repeat each U) {}
180190
func testInBetween<each U>(_: repeat each U, other: String) {}
@@ -193,6 +203,31 @@ do {
193203
_ = test[data: repeat each args, 0, ""]
194204

195205
test[data: repeat each args, "", 42] = 0
206+
207+
do {
208+
let first = ""
209+
let second = ""
210+
let third = 42
211+
212+
_ = test[simpleTuple: (repeat each args)]
213+
_ = test[simpleTuple: (repeat each args, extra)]
214+
_ = test[simpleTuple: (first, second)]
215+
_ = test[compoundTuple: (first, repeat each args)]
216+
_ = test[compoundTuple: (first, repeat each args, extra)]
217+
_ = test[compoundTuple: (first, second, third)]
218+
}
219+
220+
do {
221+
func testRef<each T>() -> (repeat each T, String) { fatalError() }
222+
func testResult<each T>() -> (repeat each T) { fatalError() }
223+
224+
func experiment1<each U>() -> (repeat each U, String) {
225+
testResult() // Ok
226+
}
227+
228+
func experiment2<each U>(_: () -> (repeat each U)) -> (repeat each U) { fatalError() }
229+
let _: (Int, String) = experiment2(testRef) // Ok
230+
}
196231
}
197232
}
198233

0 commit comments

Comments
 (0)