Skip to content

Commit ac7efc1

Browse files
authored
Merge pull request #41935 from slavapestov/rqm-concrete-contraction-unrelated-alias
RequirementMachine: Another silly GenericSignatureBuilder compatibility hack for concrete contraction
2 parents 687beac + dc82433 commit ac7efc1

File tree

2 files changed

+132
-1
lines changed

2 files changed

+132
-1
lines changed

lib/AST/RequirementMachine/ConcreteContraction.cpp

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ class ConcreteContraction {
173173
Type substType(Type type) const;
174174
Requirement substRequirement(const Requirement &req) const;
175175

176+
bool preserveSameTypeRequirement(const Requirement &req) const;
177+
178+
bool hasResolvedMemberTypeOfInterestingParameter(Type t) const;
179+
176180
public:
177181
ConcreteContraction(bool debug) : Debug(debug) {}
178182

@@ -384,6 +388,73 @@ ConcreteContraction::substRequirement(const Requirement &req) const {
384388
}
385389
}
386390

391+
bool ConcreteContraction::
392+
hasResolvedMemberTypeOfInterestingParameter(Type type) const {
393+
return type.findIf([&](Type t) -> bool {
394+
if (auto *memberTy = t->getAs<DependentMemberType>()) {
395+
if (memberTy->getAssocType() == nullptr)
396+
return false;
397+
398+
auto baseTy = memberTy->getBase();
399+
if (auto *genericParam = baseTy->getAs<GenericTypeParamType>()) {
400+
GenericParamKey key(genericParam);
401+
402+
Type concreteType;
403+
{
404+
auto found = ConcreteTypes.find(key);
405+
if (found != ConcreteTypes.end() && found->second.size() == 1)
406+
return true;
407+
}
408+
409+
Type superclass;
410+
{
411+
auto found = Superclasses.find(key);
412+
if (found != Superclasses.end() && found->second.size() == 1)
413+
return true;
414+
}
415+
}
416+
}
417+
418+
return false;
419+
});
420+
}
421+
422+
/// Another silly GenericSignatureBuilder compatibility hack.
423+
///
424+
/// Consider this code:
425+
///
426+
/// class C<T> {
427+
/// typealias A = T
428+
/// }
429+
///
430+
/// protocol P {
431+
/// associatedtype A
432+
/// }
433+
///
434+
/// func f<X, T>(_: X, _: T) where X : P, X : C<T>, X.A == T {}
435+
///
436+
/// The GenericSignatureBuilder would introduce an equivalence between
437+
/// typealias A in class C and associatedtype A in protocol P, so the
438+
/// requirement 'X.A == T' would effectively constrain _both_.
439+
///
440+
/// Simulate this by keeping both the original and substituted same-type
441+
/// requirement in a narrow case.
442+
bool ConcreteContraction::preserveSameTypeRequirement(
443+
const Requirement &req) const {
444+
if (req.getKind() != RequirementKind::SameType)
445+
return false;
446+
447+
if (Superclasses.find(req.getFirstType()->getRootGenericParam())
448+
== Superclasses.end())
449+
return false;
450+
451+
if (hasResolvedMemberTypeOfInterestingParameter(req.getFirstType()) ||
452+
hasResolvedMemberTypeOfInterestingParameter(req.getSecondType()))
453+
return false;
454+
455+
return true;
456+
}
457+
387458
/// Substitute all occurrences of generic parameters subject to superclass
388459
/// or concrete type requirements with their corresponding superclass or
389460
/// concrete type.
@@ -506,6 +577,18 @@ bool ConcreteContraction::performConcreteContraction(
506577
llvm::dbgs() << "\n";
507578
}
508579

580+
if (preserveSameTypeRequirement(req.req)) {
581+
if (Debug) {
582+
llvm::dbgs() << "@ Preserving original requirement: ";
583+
req.req.dump(llvm::dbgs());
584+
llvm::dbgs() << "\n";
585+
}
586+
587+
// Make the duplicated requirement 'inferred' so that we don't diagnose
588+
// it as redundant.
589+
result.push_back({req.req, SourceLoc(), /*inferred=*/true});
590+
}
591+
509592
// Substitute the requirement.
510593
Optional<Requirement> substReq = substRequirement(req.req);
511594

@@ -519,7 +602,7 @@ bool ConcreteContraction::performConcreteContraction(
519602
llvm::dbgs() << "\n";
520603
}
521604

522-
return false;
605+
continue;
523606
}
524607

525608
if (Debug) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// R/UN: %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 -disable-requirement-machine-concrete-contraction 2>&1 | %FileCheck %s
3+
4+
// Another GenericSignatureBuilder oddity, reduced from RxSwift.
5+
//
6+
// The requirements 'Proxy.Parent == P' and 'Proxy.Delegate == D' in the
7+
// init() below refer to both the typealias and the associated type,
8+
// despite the class being unrelated to the protocol; it just happens to
9+
// define typealiases with the same name.
10+
//
11+
// In the Requirement Machine, the concrete contraction pre-processing
12+
// pass would eagerly substitute the concrete type into these two
13+
// requirements, producing the useless requirements 'P == P' and 'D == D'.
14+
//
15+
// Make sure concrete contraction keeps these requirements as-is by
16+
// checking the generic signature with and without concrete contraction.
17+
18+
class GenericDelegateProxy<P : AnyObject, D> {
19+
typealias Parent = P
20+
typealias Delegate = D
21+
22+
// CHECK-LABEL: .GenericDelegateProxy.init(_:)@
23+
// CHECK-NEXT: <P, D, Proxy where P == Proxy.[DelegateProxyType]Parent, D == Proxy.[DelegateProxyType]Delegate, Proxy : GenericDelegateProxy<P, D>, Proxy : DelegateProxyType>
24+
init<Proxy: DelegateProxyType>(_: Proxy.Type)
25+
where Proxy: GenericDelegateProxy<P, D>,
26+
Proxy.Parent == P,
27+
Proxy.Delegate == D {}
28+
}
29+
30+
class SomeClass {}
31+
struct SomeStruct {}
32+
33+
class ConcreteDelegateProxy {
34+
typealias Parent = SomeClass
35+
typealias Delegate = SomeStruct
36+
37+
// CHECK-LABEL: .ConcreteDelegateProxy.init(_:_:_:)@
38+
// CHECK-NEXT: <P, D, Proxy where P == Proxy.[DelegateProxyType]Parent, D == Proxy.[DelegateProxyType]Delegate, Proxy : ConcreteDelegateProxy, Proxy : DelegateProxyType>
39+
init<P, D, Proxy: DelegateProxyType>(_: P, _: D, _: Proxy.Type)
40+
where Proxy: ConcreteDelegateProxy,
41+
Proxy.Parent == P,
42+
Proxy.Delegate == D {}
43+
}
44+
45+
protocol DelegateProxyType {
46+
associatedtype Parent : AnyObject
47+
associatedtype Delegate
48+
}

0 commit comments

Comments
 (0)