Skip to content

Commit 05aeeff

Browse files
committed
RequirementMachine: Reconstitute sugar in trivial cases
The Requirement Machine operates on canonical types internally and erases sugared types appearing in generic requirements as a result. For trivial cases like Array<T> vs [T], we can use the existing TypeBase::reconstituteSugar() utility to produce a more aesthetically-pleasing generic signature.
1 parent e0c10d4 commit 05aeeff

7 files changed

+106
-31
lines changed

lib/AST/RequirementMachine/RequirementBuilder.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ class RequirementBuilder {
127127
const RewriteSystem &System;
128128
const PropertyMap &Map;
129129
TypeArrayView<GenericTypeParamType> GenericParams;
130+
bool ReconstituteSugar;
130131
bool Debug;
131132

132133
// Temporary state populated by addRequirementRules() and
@@ -139,8 +140,11 @@ class RequirementBuilder {
139140
std::vector<ProtocolTypeAlias> Aliases;
140141

141142
RequirementBuilder(const RewriteSystem &system, const PropertyMap &map,
142-
TypeArrayView<GenericTypeParamType> genericParams)
143-
: System(system), Map(map), GenericParams(genericParams),
143+
TypeArrayView<GenericTypeParamType> genericParams,
144+
bool reconstituteSugar)
145+
: System(system), Map(map),
146+
GenericParams(genericParams),
147+
ReconstituteSugar(reconstituteSugar),
144148
Debug(System.getDebugOptions().contains(DebugFlags::Minimization)) {}
145149

146150
void addRequirementRules(ArrayRef<unsigned> rules);
@@ -186,11 +190,14 @@ void RequirementBuilder::addRequirementRules(ArrayRef<unsigned> rules) {
186190
if (prop->getConcreteType()->hasError())
187191
return;
188192

189-
auto superclassType = Map.getTypeFromSubstitutionSchema(
193+
Type superclassType = Map.getTypeFromSubstitutionSchema(
190194
prop->getConcreteType(),
191195
prop->getSubstitutions(),
192196
GenericParams, MutableTerm());
193197

198+
if (ReconstituteSugar)
199+
superclassType = superclassType->reconstituteSugar(/*recursive=*/true);
200+
194201
Reqs.emplace_back(RequirementKind::Superclass,
195202
subjectType, superclassType);
196203
return;
@@ -209,11 +216,14 @@ void RequirementBuilder::addRequirementRules(ArrayRef<unsigned> rules) {
209216
if (prop->getConcreteType()->hasError())
210217
return;
211218

212-
auto concreteType = Map.getTypeFromSubstitutionSchema(
219+
Type concreteType = Map.getTypeFromSubstitutionSchema(
213220
prop->getConcreteType(),
214221
prop->getSubstitutions(),
215222
GenericParams, MutableTerm());
216223

224+
if (ReconstituteSugar)
225+
concreteType = concreteType->reconstituteSugar(/*recursive=*/true);
226+
217227
auto &component = Components[subjectType.getPointer()];
218228
assert(!component.ConcreteType);
219229
component.ConcreteType = concreteType;
@@ -281,10 +291,14 @@ void RequirementBuilder::addTypeAliasRules(ArrayRef<unsigned> rules) {
281291
if (prop->getConcreteType()->hasError())
282292
continue;
283293

284-
auto concreteType = Map.getTypeFromSubstitutionSchema(
294+
Type concreteType = Map.getTypeFromSubstitutionSchema(
285295
prop->getConcreteType(),
286296
prop->getSubstitutions(),
287297
GenericParams, MutableTerm());
298+
299+
if (ReconstituteSugar)
300+
concreteType = concreteType->reconstituteSugar(/*recursive=*/true);
301+
288302
auto &component = Components[subjectType.getPointer()];
289303
assert(!component.ConcreteType);
290304
Components[subjectType.getPointer()].ConcreteType = concreteType;
@@ -345,9 +359,10 @@ RequirementMachine::buildRequirementsFromRules(
345359
ArrayRef<unsigned> requirementRules,
346360
ArrayRef<unsigned> typeAliasRules,
347361
TypeArrayView<GenericTypeParamType> genericParams,
362+
bool reconstituteSugar,
348363
std::vector<Requirement> &reqs,
349364
std::vector<ProtocolTypeAlias> &aliases) const {
350-
RequirementBuilder builder(System, Map, genericParams);
365+
RequirementBuilder builder(System, Map, genericParams, reconstituteSugar);
351366

352367
builder.addRequirementRules(requirementRules);
353368
builder.addTypeAliasRules(typeAliasRules);

lib/AST/RequirementMachine/RequirementMachine.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class RequirementMachine final {
112112
ArrayRef<unsigned> requirementRules,
113113
ArrayRef<unsigned> typeAliasRules,
114114
TypeArrayView<GenericTypeParamType> genericParams,
115+
bool reconstituteSugar,
115116
std::vector<Requirement> &reqs,
116117
std::vector<ProtocolTypeAlias> &aliases) const;
117118

@@ -149,7 +150,8 @@ class RequirementMachine final {
149150
llvm::DenseMap<const ProtocolDecl *, RequirementSignature>
150151
computeMinimalProtocolRequirements();
151152

152-
std::vector<Requirement> computeMinimalGenericSignatureRequirements();
153+
std::vector<Requirement>
154+
computeMinimalGenericSignatureRequirements(bool reconstituteSugar);
153155

154156
std::string getRuleAsStringForDiagnostics(unsigned ruleID) const;
155157

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ RequirementMachine::computeMinimalProtocolRequirements() {
7272
buildRequirementsFromRules(entry.Requirements,
7373
entry.TypeAliases,
7474
genericParams,
75+
/*reconstituteSugar=*/true,
7576
reqs, aliases);
7677

7778
result[proto] = RequirementSignature(ctx.AllocateCopy(reqs),
@@ -180,7 +181,8 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
180181

181182
/// Builds the top-level generic signature requirements for this rewrite system.
182183
std::vector<Requirement>
183-
RequirementMachine::computeMinimalGenericSignatureRequirements() {
184+
RequirementMachine::computeMinimalGenericSignatureRequirements(
185+
bool reconstituteSugar) {
184186
assert(System.getProtocols().empty() &&
185187
"Not a top-level generic signature rewrite system");
186188
assert(!Params.empty() &&
@@ -199,7 +201,7 @@ RequirementMachine::computeMinimalGenericSignatureRequirements() {
199201
std::vector<ProtocolTypeAlias> aliases;
200202

201203
buildRequirementsFromRules(rules, ArrayRef<unsigned>(), getGenericParams(),
202-
reqs, aliases);
204+
reconstituteSugar, reqs, aliases);
203205
assert(aliases.empty());
204206

205207
return reqs;
@@ -281,8 +283,11 @@ AbstractGenericSignatureRequestRQM::evaluate(
281283
machine->initWithWrittenRequirements(genericParams, requirements);
282284
machine->checkCompletionResult(status.first);
283285

286+
// We pass reconstituteSugar=false to ensure that if the original
287+
// requirements were canonical, the final signature remains canonical.
284288
auto minimalRequirements =
285-
machine->computeMinimalGenericSignatureRequirements();
289+
machine->computeMinimalGenericSignatureRequirements(
290+
/*reconstituteSugar=*/false);
286291

287292
auto result = GenericSignature::get(genericParams, minimalRequirements);
288293
bool hadError = machine->hadError();
@@ -423,7 +428,8 @@ InferredGenericSignatureRequestRQM::evaluate(
423428
}
424429

425430
auto minimalRequirements =
426-
machine->computeMinimalGenericSignatureRequirements();
431+
machine->computeMinimalGenericSignatureRequirements(
432+
/*reconstituteSugar=*/true);
427433

428434
auto result = GenericSignature::get(genericParams, minimalRequirements);
429435
bool hadError = machine->hadError();

test/Constraints/rdar39931339.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-typecheck-verify-swift
2+
// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
23

34
protocol P {}
45

@@ -17,10 +18,14 @@ extension S where T == Float { // expected-note {{requirement specified as 'T' =
1718

1819
class A<T, U> {}
1920

21+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=A
22+
// CHECK-NEXT: Generic signature: <T, U where T == [U], U : P>
2023
extension A where T == [U], U: P {
2124
typealias S1 = Int
2225
}
2326

27+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=A
28+
// CHECK-NEXT: Generic signature: <T, U where T == [Int], U == Int>
2429
extension A where T == [U], U == Int {
2530
// expected-note@-1 {{requirement specified as 'T' == '[Int]' [with T = [String]]}}
2631
typealias S2 = Int

test/Generics/conditional_requirement_inference_in_protocol.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures 2>&1 | %FileCheck %s
33

44
// CHECK-LABEL: conditional_requirement_inference_in_protocol.(file).Good@
5-
// CHECK-LABEL: Requirement signature: <Self where Self.[Good]T == Array<Self.[Good]U>, Self.[Good]U : Equatable>
5+
// CHECK-LABEL: Requirement signature: <Self where Self.[Good]T == [Self.[Good]U], Self.[Good]U : Equatable>
66

77
protocol Good {
88
associatedtype T : Equatable // expected-warning {{redundant conformance constraint 'Self.T' : 'Equatable'}}
99
associatedtype U : Equatable where T == Array<U> // expected-note {{conformance constraint 'Self.T' : 'Equatable' implied here}}
1010
}
1111

1212
// CHECK-LABEL: conditional_requirement_inference_in_protocol.(file).Bad@
13-
// CHECK-LABEL: Requirement signature: <Self where Self.[Bad]T == Array<Self.[Bad]U>>
13+
// CHECK-LABEL: Requirement signature: <Self where Self.[Bad]T == [Self.[Bad]U]>
1414

1515
protocol Bad {
1616
associatedtype T : Equatable // expected-warning {{redundant conformance constraint 'Self.T' : 'Equatable'}}

test/Generics/minimize_superclass_unification_generic_2.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ protocol Pba {
5959
// CHECK-NEXT: Requirement signature: <Self where
6060

6161
// CHECK-SAME: Self.[Pac]A1 == String,
62-
// CHECK-SAME: Self.[Pac]A2 == (Self.[Pac]C1) -> Array<Self.[Pac]C2.[Sequence]Element>,
62+
// CHECK-SAME: Self.[Pac]A2 == (Self.[Pac]C1) -> [Self.[Pac]C2.[Sequence]Element],
6363
// CHECK-SAME: Self.[Pac]A3 == Int,
6464

6565
// CHECK-SAME: Self.[Pac]C2 : Sequence,
@@ -82,7 +82,7 @@ protocol Pac {
8282
// CHECK-NEXT: Requirement signature: <Self where
8383

8484
// CHECK-SAME: Self.[Pca]A1 == String,
85-
// CHECK-SAME: Self.[Pca]A2 == (Self.[Pca]C1) -> Array<Self.[Pca]C2.[Sequence]Element>,
85+
// CHECK-SAME: Self.[Pca]A2 == (Self.[Pca]C1) -> [Self.[Pca]C2.[Sequence]Element],
8686
// CHECK-SAME: Self.[Pca]A3 == Int,
8787

8888
// CHECK-SAME: Self.[Pca]C2 : Sequence,
@@ -106,7 +106,7 @@ protocol Pca {
106106

107107
// CHECK-SAME: Self.[Pbc]B1 == String,
108108
// CHECK-SAME: Self.[Pbc]B2 == Self.[Pbc]C1,
109-
// CHECK-SAME: Self.[Pbc]B3 == Array<Self.[Pbc]C2.[Sequence]Element>,
109+
// CHECK-SAME: Self.[Pbc]B3 == [Self.[Pbc]C2.[Sequence]Element],
110110
// CHECK-SAME: Self.[Pbc]B4 == Self.[Pbc]C3,
111111

112112
// CHECK-SAME: Self.[Pbc]C2 : Sequence,
@@ -131,7 +131,7 @@ protocol Pbc {
131131

132132
// CHECK-SAME: Self.[Pcb]B1 == String,
133133
// CHECK-SAME: Self.[Pcb]B2 == Self.[Pcb]C1,
134-
// CHECK-SAME: Self.[Pcb]B3 == Array<Self.[Pcb]C2.[Sequence]Element>,
134+
// CHECK-SAME: Self.[Pcb]B3 == [Self.[Pcb]C2.[Sequence]Element],
135135
// CHECK-SAME: Self.[Pcb]B4 == Self.[Pcb]C3,
136136

137137
// CHECK-SAME: Self.[Pcb]C2 : Sequence,
@@ -160,12 +160,12 @@ protocol Pcb {
160160
// CHECK-NEXT: Requirement signature: <Self where
161161

162162
// CHECK-SAME: Self.[Pabc]A1 == String,
163-
// CHECK-SAME: Self.[Pabc]A2 == (Self.[Pabc]B2) -> Array<Self.[Pabc]C2.[Sequence]Element>,
163+
// CHECK-SAME: Self.[Pabc]A2 == (Self.[Pabc]B2) -> [Self.[Pabc]C2.[Sequence]Element],
164164
// CHECK-SAME: Self.[Pabc]A3 == Int,
165165

166166
// CHECK-SAME: Self.[Pabc]B1 == String,
167167
// CHECK-SAME: Self.[Pabc]B2 == Self.[Pabc]C1,
168-
// CHECK-SAME: Self.[Pabc]B3 == Array<Self.[Pabc]C2.[Sequence]Element>,
168+
// CHECK-SAME: Self.[Pabc]B3 == [Self.[Pabc]C2.[Sequence]Element],
169169
// CHECK-SAME: Self.[Pabc]B4 == Self.[Pabc]C3,
170170

171171
// CHECK-SAME: Self.[Pabc]C2 : Sequence,
@@ -193,12 +193,12 @@ protocol Pabc {
193193
// CHECK-NEXT: Requirement signature: <Self where
194194

195195
// CHECK-SAME: Self.[Pacb]A1 == String,
196-
// CHECK-SAME: Self.[Pacb]A2 == (Self.[Pacb]B2) -> Array<Self.[Pacb]C2.[Sequence]Element>,
196+
// CHECK-SAME: Self.[Pacb]A2 == (Self.[Pacb]B2) -> [Self.[Pacb]C2.[Sequence]Element],
197197
// CHECK-SAME: Self.[Pacb]A3 == Int,
198198

199199
// CHECK-SAME: Self.[Pacb]B1 == String,
200200
// CHECK-SAME: Self.[Pacb]B2 == Self.[Pacb]C1,
201-
// CHECK-SAME: Self.[Pacb]B3 == Array<Self.[Pacb]C2.[Sequence]Element>,
201+
// CHECK-SAME: Self.[Pacb]B3 == [Self.[Pacb]C2.[Sequence]Element],
202202
// CHECK-SAME: Self.[Pacb]B4 == Self.[Pacb]C3,
203203

204204
// CHECK-SAME: Self.[Pacb]C2 : Sequence,
@@ -226,12 +226,12 @@ protocol Pacb {
226226
// CHECK-NEXT: Requirement signature: <Self where
227227

228228
// CHECK-SAME: Self.[Pbac]A1 == String,
229-
// CHECK-SAME: Self.[Pbac]A2 == (Self.[Pbac]B2) -> Array<Self.[Pbac]C2.[Sequence]Element>,
229+
// CHECK-SAME: Self.[Pbac]A2 == (Self.[Pbac]B2) -> [Self.[Pbac]C2.[Sequence]Element],
230230
// CHECK-SAME: Self.[Pbac]A3 == Int,
231231

232232
// CHECK-SAME: Self.[Pbac]B1 == String,
233233
// CHECK-SAME: Self.[Pbac]B2 == Self.[Pbac]C1,
234-
// CHECK-SAME: Self.[Pbac]B3 == Array<Self.[Pbac]C2.[Sequence]Element>,
234+
// CHECK-SAME: Self.[Pbac]B3 == [Self.[Pbac]C2.[Sequence]Element],
235235
// CHECK-SAME: Self.[Pbac]B4 == Self.[Pbac]C3,
236236

237237
// CHECK-SAME: Self.[Pbac]C2 : Sequence,
@@ -259,12 +259,12 @@ protocol Pbac {
259259
// CHECK-NEXT: Requirement signature: <Self where
260260

261261
// CHECK-SAME: Self.[Pbca]A1 == String,
262-
// CHECK-SAME: Self.[Pbca]A2 == (Self.[Pbca]B2) -> Array<Self.[Pbca]C2.[Sequence]Element>,
262+
// CHECK-SAME: Self.[Pbca]A2 == (Self.[Pbca]B2) -> [Self.[Pbca]C2.[Sequence]Element],
263263
// CHECK-SAME: Self.[Pbca]A3 == Int,
264264

265265
// CHECK-SAME: Self.[Pbca]B1 == String,
266266
// CHECK-SAME: Self.[Pbca]B2 == Self.[Pbca]C1,
267-
// CHECK-SAME: Self.[Pbca]B3 == Array<Self.[Pbca]C2.[Sequence]Element>,
267+
// CHECK-SAME: Self.[Pbca]B3 == [Self.[Pbca]C2.[Sequence]Element],
268268
// CHECK-SAME: Self.[Pbca]B4 == Self.[Pbca]C3,
269269

270270
// CHECK-SAME: Self.[Pbca]C2 : Sequence,
@@ -292,12 +292,12 @@ protocol Pbca {
292292
// CHECK-NEXT: Requirement signature: <Self where
293293

294294
// CHECK-SAME: Self.[Pcab]A1 == String,
295-
// CHECK-SAME: Self.[Pcab]A2 == (Self.[Pcab]B2) -> Array<Self.[Pcab]C2.[Sequence]Element>,
295+
// CHECK-SAME: Self.[Pcab]A2 == (Self.[Pcab]B2) -> [Self.[Pcab]C2.[Sequence]Element],
296296
// CHECK-SAME: Self.[Pcab]A3 == Int,
297297

298298
// CHECK-SAME: Self.[Pcab]B1 == String,
299299
// CHECK-SAME: Self.[Pcab]B2 == Self.[Pcab]C1,
300-
// CHECK-SAME: Self.[Pcab]B3 == Array<Self.[Pcab]C2.[Sequence]Element>,
300+
// CHECK-SAME: Self.[Pcab]B3 == [Self.[Pcab]C2.[Sequence]Element],
301301
// CHECK-SAME: Self.[Pcab]B4 == Self.[Pcab]C3,
302302

303303
// CHECK-SAME: Self.[Pcab]C2 : Sequence,
@@ -325,12 +325,12 @@ protocol Pcab {
325325
// CHECK-NEXT: Requirement signature: <Self where
326326

327327
// CHECK-SAME: Self.[Pcba]A1 == String,
328-
// CHECK-SAME: Self.[Pcba]A2 == (Self.[Pcba]B2) -> Array<Self.[Pcba]C2.[Sequence]Element>,
328+
// CHECK-SAME: Self.[Pcba]A2 == (Self.[Pcba]B2) -> [Self.[Pcba]C2.[Sequence]Element],
329329
// CHECK-SAME: Self.[Pcba]A3 == Int,
330330

331331
// CHECK-SAME: Self.[Pcba]B1 == String,
332332
// CHECK-SAME: Self.[Pcba]B2 == Self.[Pcba]C1,
333-
// CHECK-SAME: Self.[Pcba]B3 == Array<Self.[Pcba]C2.[Sequence]Element>,
333+
// CHECK-SAME: Self.[Pcba]B3 == [Self.[Pcba]C2.[Sequence]Element],
334334
// CHECK-SAME: Self.[Pcba]B4 == Self.[Pcba]C3,
335335

336336
// CHECK-SAME: Self.[Pcba]C2 : Sequence,
@@ -438,7 +438,7 @@ protocol PaQc {
438438
// CHECK-NEXT: Requirement signature: <Self where
439439

440440
// CHECK-SAME: Self.[PcQa]A1 == String,
441-
// CHECK-SAME: Self.[PcQa]A2 == (Self.[PcQa]T.[Pc]C1) -> Array<Self.[PcQa]T.[Pc]C2.[Sequence]Element>,
441+
// CHECK-SAME: Self.[PcQa]A2 == (Self.[PcQa]T.[Pc]C1) -> [Self.[PcQa]T.[Pc]C2.[Sequence]Element],
442442
// CHECK-SAME: Self.[PcQa]A3 == Int,
443443

444444
// CHECK-SAME: Self.[PcQa]T : Pc>
@@ -473,7 +473,7 @@ protocol PbQc {
473473

474474
// CHECK-SAME: Self.[PcQb]B1 == String,
475475
// CHECK-SAME: Self.[PcQb]B2 == Self.[PcQb]T.[Pc]C1,
476-
// CHECK-SAME: Self.[PcQb]B3 == Array<Self.[PcQb]T.[Pc]C2.[Sequence]Element>,
476+
// CHECK-SAME: Self.[PcQb]B3 == [Self.[PcQb]T.[Pc]C2.[Sequence]Element],
477477
// CHECK-SAME: Self.[PcQb]B4 == Self.[PcQb]T.[Pc]C3,
478478

479479
// CHECK-SAME: Self.[PcQb]T : Pc>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
2+
3+
// The Requirement Machine works with canonical types internally, so make sure
4+
// we reconstitute sugar in trivial cases before surfacing the generic
5+
// signature to the user.
6+
7+
struct G<X, Y, Z> {}
8+
9+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
10+
// CHECK-NEXT: Generic signature: <X, Y, Z where X == Y?>
11+
extension G where X == Optional<Y> {}
12+
13+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
14+
// CHECK-NEXT: Generic signature: <X, Y, Z where X == [Y]>
15+
extension G where X == Array<Y> {}
16+
17+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
18+
// CHECK-NEXT: Generic signature: <X, Y, Z where X == [Y : Z], Y : Hashable>
19+
extension G where X == Dictionary<Y, Z> {}
20+
21+
// We don't do () => Swift.Void.
22+
23+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
24+
// CHECK-NEXT: Generic signature: <X, Y, Z where X == ()>
25+
extension G where X == () {}
26+
27+
// Now make sure we do the same for superclass requirements.
28+
29+
class C<T> {}
30+
31+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
32+
// CHECK-NEXT: Generic signature: <X, Y, Z where X : C<Y?>>
33+
extension G where X : C<Optional<Y>> {}
34+
35+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
36+
// CHECK-NEXT: Generic signature: <X, Y, Z where X : C<[Y]>>
37+
extension G where X : C<Array<Y>> {}
38+
39+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
40+
// CHECK-NEXT: Generic signature: <X, Y, Z where X : C<[Y : Z]>, Y : Hashable>
41+
extension G where X : C<Dictionary<Y, Z>> {}
42+
43+
// We don't do () => Swift.Void.
44+
45+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
46+
// CHECK-NEXT: Generic signature: <X, Y, Z where X : C<()>>
47+
extension G where X : C<()> {}

0 commit comments

Comments
 (0)