Skip to content

Commit 589c1a3

Browse files
committed
RequirementMachine: A couple of improvements to concrete contraction
- Allow duplicate concrete type and superclass requirements on the same generic parameter, as long as they're identical. This can arise if requirement inference infers a requirement which the user then explicitly re-states. - If duplicate requirements are found that name different types, drop only that generic parameter from consideration without giving up entirely. - If a generic parameter is subject to both a concrete type and a superclass requirement, proceed with the concrete type requirement since it is more specific instead of giving up.
1 parent 0d09424 commit 589c1a3

File tree

2 files changed

+48
-61
lines changed

2 files changed

+48
-61
lines changed

lib/AST/RequirementMachine/ConcreteContraction.cpp

Lines changed: 26 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,10 @@ namespace {
157157
class ConcreteContraction {
158158
bool Debug;
159159

160-
llvm::SmallDenseMap<GenericParamKey, Type> ConcreteTypes;
161-
llvm::SmallDenseMap<GenericParamKey, Type> Superclasses;
160+
llvm::SmallDenseMap<GenericParamKey,
161+
llvm::SmallDenseSet<Type, 1>> ConcreteTypes;
162+
llvm::SmallDenseMap<GenericParamKey,
163+
llvm::SmallDenseSet<Type, 1>> Superclasses;
162164
llvm::SmallDenseMap<GenericParamKey,
163165
llvm::SmallVector<ProtocolDecl *, 1>> Conformances;
164166

@@ -296,20 +298,22 @@ Type ConcreteContraction::substTypeParameter(
296298
Type concreteType;
297299
{
298300
auto found = ConcreteTypes.find(key);
299-
if (found != ConcreteTypes.end())
300-
concreteType = found->second;
301+
if (found != ConcreteTypes.end() && found->second.size() == 1)
302+
concreteType = *found->second.begin();
301303
}
302304

303305
Type superclass;
304306
{
305307
auto found = Superclasses.find(key);
306-
if (found != Superclasses.end())
307-
superclass = found->second;
308+
if (found != Superclasses.end() && found->second.size() == 1)
309+
superclass = *found->second.begin();
308310
}
309311

310312
if (!concreteType && !superclass)
311313
return type;
312314

315+
// If we have both, prefer the concrete type requirement since it is more
316+
// specific.
313317
if (!concreteType) {
314318
assert(superclass);
315319

@@ -434,35 +438,15 @@ bool ConcreteContraction::performConcreteContraction(
434438
if (constraintType->isTypeParameter())
435439
break;
436440

437-
auto entry = std::make_pair(GenericParamKey(genericParam),
438-
constraintType);
439-
bool inserted = ConcreteTypes.insert(entry).second;
440-
if (!inserted) {
441-
if (Debug) {
442-
llvm::dbgs() << "@ Concrete contraction cannot proceed: "
443-
<< "duplicate concrete type requirements\n";
444-
}
445-
return false;
446-
}
447-
441+
ConcreteTypes[GenericParamKey(genericParam)].insert(constraintType);
448442
break;
449443
}
450444
case RequirementKind::Superclass: {
451445
auto constraintType = req.req.getSecondType();
452446
assert(!constraintType->isTypeParameter() &&
453447
"You forgot to call desugarRequirement()");
454448

455-
auto entry = std::make_pair(GenericParamKey(genericParam),
456-
constraintType);
457-
bool inserted = Superclasses.insert(entry).second;
458-
if (!inserted) {
459-
if (Debug) {
460-
llvm::dbgs() << "@ Concrete contraction cannot proceed: "
461-
<< "duplicate superclass requirements\n";
462-
}
463-
return false;
464-
}
465-
449+
Superclasses[GenericParamKey(genericParam)].insert(constraintType);
466450
break;
467451
}
468452
case RequirementKind::Conformance: {
@@ -484,10 +468,10 @@ bool ConcreteContraction::performConcreteContraction(
484468
for (const auto &pair : Conformances) {
485469
auto subjectType = pair.first;
486470
auto found = Superclasses.find(subjectType);
487-
if (found == Superclasses.end())
471+
if (found == Superclasses.end() || found->second.size() != 1)
488472
continue;
489473

490-
auto superclassTy = found->second;
474+
auto superclassTy = *found->second.begin();
491475

492476
for (const auto *proto : pair.second) {
493477
if (auto otherSuperclassTy = proto->getSuperclass()) {
@@ -511,35 +495,27 @@ bool ConcreteContraction::performConcreteContraction(
511495
if (ConcreteTypes.empty() && Superclasses.empty())
512496
return false;
513497

514-
// If a generic parameter is subject to both a concrete type and superclass
515-
// requirement, bail out because we're not smart enough to figure out what's
516-
// going on.
517-
for (auto pair : ConcreteTypes) {
518-
auto subjectType = pair.first;
519-
520-
if (Superclasses.find(subjectType) != Superclasses.end()) {
521-
if (Debug) {
522-
llvm::dbgs() << "@ Concrete contraction cannot proceed; "
523-
<< "τ_" << subjectType.Depth << "_" << subjectType.Index
524-
<< " has both a concrete type and superclass requirement";
525-
}
526-
return false;
527-
}
528-
}
529-
530498
if (Debug) {
531499
llvm::dbgs() << "@ Concrete types: @\n";
532500
for (auto pair : ConcreteTypes) {
533501
llvm::dbgs() << "- τ_" << pair.first.Depth
534-
<< "_" << pair.first.Index << " == "
535-
<< pair.second << "\n";
502+
<< "_" << pair.first.Index;
503+
if (pair.second.size() == 1) {
504+
llvm::dbgs() << " == " << *pair.second.begin() << "\n";
505+
} else {
506+
llvm::dbgs() << " has duplicate concrete type requirements\n";
507+
}
536508
}
537509

538510
llvm::dbgs() << "@ Superclasses: @\n";
539511
for (auto pair : Superclasses) {
540512
llvm::dbgs() << "- τ_" << pair.first.Depth
541-
<< "_" << pair.first.Index << " : "
542-
<< pair.second << "\n";
513+
<< "_" << pair.first.Index;
514+
if (pair.second.size() == 1) {
515+
llvm::dbgs() << " : " << *pair.second.begin() << "\n";
516+
} else {
517+
llvm::dbgs() << " has duplicate superclass requirements\n";
518+
}
543519
}
544520
}
545521

test/Generics/derived_via_concrete.swift

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

44
// FIXME: Both RUN lines should pass 'on' once diagnostics are implemented.
55

@@ -22,42 +22,53 @@ protocol W {
2222
associatedtype T : AnyObject
2323
}
2424

25-
// CHECK: Generic signature: <A, B where A : X<B>, B : P>
25+
// CHECK-LABEL: .derivedViaConcreteX1@
26+
// CHECK-NEXT: Generic signature: <A, B where A : X<B>, B : P>
2627
func derivedViaConcreteX1<A, B>(_: A, _: B)
2728
where A : U, A : X<B> {}
2829
// expected-warning@-1 {{redundant conformance constraint 'A' : 'U'}}
2930
// expected-note@-2 {{conformance constraint 'A' : 'U' implied here}}
3031

31-
// CHECK: Generic signature: <A, B where A : X<B>, B : P>
32+
// CHECK-LABEL: .derivedViaConcreteX2@
33+
// CHECK-NEXT: Generic signature: <A, B where A : X<B>, B : P>
3234
func derivedViaConcreteX2<A, B>(_: A, _: B)
3335
where A : U, B : P, A : X<B> {}
3436
// expected-warning@-1 {{redundant conformance constraint 'A' : 'U'}}
3537
// expected-note@-2 {{conformance constraint 'A' : 'U' implied here}}
3638

37-
// CHECK: Generic signature: <A, B where A : Y<B>, B : C>
39+
// CHECK-LABEL: .derivedViaConcreteY1@
40+
// CHECK-NEXT: Generic signature: <A, B where A : Y<B>, B : C>
3841
func derivedViaConcreteY1<A, B>(_: A, _: B)
3942
where A : V, A : Y<B> {}
4043
// expected-warning@-1 {{redundant conformance constraint 'A' : 'V'}}
4144
// expected-note@-2 {{conformance constraint 'A' : 'V' implied here}}
4245

43-
// CHECK: Generic signature: <A, B where A : Y<B>>
44-
//
45-
// FIXME: Should be <A, B where A : Y<B>, B : C>, but redundant
46-
// superclass requirements currently block concrete contraction.
47-
46+
// CHECK-LABEL: .derivedViaConcreteY2@
47+
// CHECK-NEXT: Generic signature: <A, B where A : Y<B>, B : C>
4848
func derivedViaConcreteY2<A, B>(_: A, _: B)
4949
where A : V, B : C, A : Y<B> {}
5050
// expected-warning@-1 {{redundant conformance constraint 'A' : 'V'}}
5151
// expected-note@-2 {{conformance constraint 'A' : 'V' implied here}}
5252

53-
// CHECK: Generic signature: <A, B where A : Z<B>, B : AnyObject>
53+
// CHECK-LABEL: .derivedViaConcreteZ1@
54+
// CHECK-NEXT: Generic signature: <A, B where A : Z<B>, B : AnyObject>
5455
func derivedViaConcreteZ1<A, B>(_: A, _: B)
5556
where A : W, A : Z<B> {}
5657
// expected-warning@-1 {{redundant conformance constraint 'A' : 'W'}}
5758
// expected-note@-2 {{conformance constraint 'A' : 'W' implied here}}
5859

59-
// CHECK: Generic signature: <A, B where A : Z<B>, B : AnyObject>
60+
// CHECK-LABEL: .derivedViaConcreteZ2@
61+
// CHECK-NEXT: Generic signature: <A, B where A : Z<B>, B : AnyObject>
6062
func derivedViaConcreteZ2<A, B>(_: A, _: B)
6163
where A : W, B : AnyObject, A : Z<B> {}
6264
// expected-warning@-1 {{redundant conformance constraint 'A' : 'W'}}
6365
// expected-note@-2 {{conformance constraint 'A' : 'W' implied here}}
66+
67+
class Base {}
68+
class Derived<T : C> : Base, V {}
69+
70+
struct G<X : Base, Y> {}
71+
72+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=G
73+
// CHECK-NEXT: Generic signature: <X, Y where X == Derived<Y>, Y : C>
74+
extension G where X == Derived<Y> {}

0 commit comments

Comments
 (0)