Skip to content

Commit d4d9c90

Browse files
authored
Merge pull request #42330 from slavapestov/rqm-superclass-vs-objc-existential-5.7
RequirementMachine: Allow 'any C & P' to satisfy a superclass requirement 'C' if 'P' is an @objc protocol [5.7]
2 parents 2e81826 + a9989fc commit d4d9c90

File tree

2 files changed

+82
-23
lines changed

2 files changed

+82
-23
lines changed

lib/AST/RequirementMachine/PropertyUnification.cpp

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,17 @@ void PropertyMap::addProperty(
658658
///
659659
/// - concrete vs superclass
660660
/// - concrete vs layout
661+
///
662+
/// Note that we allow a subclass existential 'any C & P' to satisfy a
663+
/// superclass requirement 'C' as long as 'P' is an @objc protocol.
664+
///
665+
/// This is not fully sound because 'any C & P' is not substitutable for
666+
/// 'C' if the code calls static method or required initializers on 'C',
667+
/// but existing code out there relies on this working.
668+
///
669+
/// A more refined check would ensure that 'C' had no required initializers
670+
/// and that 'P' was self-conforming; or we could ban this entirely in a
671+
/// future -swift-version mode.
661672
void PropertyMap::checkConcreteTypeRequirements() {
662673
bool debug = Debug.contains(DebugFlags::ConcreteUnification);
663674

@@ -669,6 +680,8 @@ void PropertyMap::checkConcreteTypeRequirements() {
669680
// If the concrete type is not a class and we have a superclass
670681
// requirement, we have a conflict.
671682
if (!concreteType.getConcreteType()->getClassOrBoundGenericClass() &&
683+
!(concreteType.getConcreteType()->isObjCExistentialType() &&
684+
concreteType.getConcreteType()->getSuperclass()) &&
672685
props->hasSuperclassBound()) {
673686
const auto &req = props->getSuperclassRequirement();
674687
for (auto pair : req.SuperclassRules) {
@@ -677,26 +690,35 @@ void PropertyMap::checkConcreteTypeRequirements() {
677690
}
678691
}
679692

680-
if (checkRuleOnce(concreteTypeRule)) {
681-
if (concreteType.getConcreteType()->getClassOrBoundGenericClass()) {
682-
// A rule (T.[concrete: C] => T) where C is a class type induces a rule
683-
// (T.[superclass: C] => T).
684-
auto superclassSymbol = Symbol::forSuperclass(
685-
concreteType.getConcreteType(),
686-
concreteType.getSubstitutions(),
687-
Context);
688-
689-
recordRelation(props->getKey(), concreteTypeRule,
690-
superclassSymbol, System, debug);
691-
}
693+
// If the concrete type does not satisfy a class layout constraint and
694+
// we have such a layout requirement, we have a conflict.
695+
if (!concreteType.getConcreteType()->satisfiesClassConstraint() &&
696+
props->LayoutRule &&
697+
props->Layout->isClass()) {
698+
if (checkRulePairOnce(concreteTypeRule, *props->LayoutRule))
699+
System.recordConflict(concreteTypeRule, *props->LayoutRule);
700+
}
692701

693-
// A rule (T.[concrete: C] => T) where C is a class type induces a rule
694-
// (T.[layout: L] => T), where L is either AnyObject or _NativeObject.
702+
if (checkRuleOnce(concreteTypeRule)) {
695703
if (concreteType.getConcreteType()->satisfiesClassConstraint()) {
696704
Type superclassType = concreteType.getConcreteType();
697705
if (!superclassType->getClassOrBoundGenericClass())
698706
superclassType = superclassType->getSuperclass();
699707

708+
if (superclassType) {
709+
// A rule (T.[concrete: C] => T) where C is a class type induces a rule
710+
// (T.[superclass: C] => T).
711+
auto superclassSymbol = Symbol::forSuperclass(
712+
superclassType->getCanonicalType(),
713+
concreteType.getSubstitutions(),
714+
Context);
715+
716+
recordRelation(props->getKey(), concreteTypeRule,
717+
superclassSymbol, System, debug);
718+
}
719+
720+
// A rule (T.[concrete: C] => T) where C is a class type induces a rule
721+
// (T.[layout: L] => T), where L is either AnyObject or _NativeObject.
700722
auto layoutConstraint = LayoutConstraintKind::Class;
701723
if (superclassType)
702724
if (auto *classDecl = superclassType->getClassOrBoundGenericClass())
@@ -711,15 +733,6 @@ void PropertyMap::checkConcreteTypeRequirements() {
711733
layoutSymbol, System, debug);
712734
}
713735
}
714-
715-
// If the concrete type does not satisfy a class layout constraint and
716-
// we have such a layout requirement, we have a conflict.
717-
if (!concreteType.getConcreteType()->satisfiesClassConstraint() &&
718-
props->LayoutRule &&
719-
props->Layout->isClass()) {
720-
if (checkRulePairOnce(concreteTypeRule, *props->LayoutRule))
721-
System.recordConflict(concreteTypeRule, *props->LayoutRule);
722-
}
723736
}
724737
}
725738
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %target-typecheck-verify-swift -disable-objc-attr-requires-foundation-module
2+
// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -disable-objc-attr-requires-foundation-module 2>&1 | %FileCheck %s
3+
4+
// REQUIRES: objc_interop
5+
6+
class C {}
7+
@objc protocol P1 {}
8+
protocol P2 {}
9+
protocol P3 : AnyObject {}
10+
11+
protocol Q {
12+
associatedtype A
13+
}
14+
15+
/// This is allowed, even though it's not fully sound.
16+
17+
// CHECK-LABEL: .f1@
18+
// CHECK-NEXT: Generic signature: <T where T : Q, T.[Q]A == C & P1>
19+
func f1<T : Q>(_: T) where T.A : C, T.A == any (C & P1) {}
20+
// expected-warning@-1 {{redundant superclass constraint 'T.A' : 'C'}}
21+
22+
/// These are not allowed.
23+
24+
// CHECK-LABEL: .f2@
25+
// CHECK-NEXT: Generic signature: <T where T : Q>
26+
func f2<T : Q>(_: T) where T.A : C, T.A == any P1 {}
27+
// expected-error@-1 {{no type for 'T.A' can satisfy both 'T.A : C' and 'T.A == any P1'}}
28+
29+
// CHECK-LABEL: .f3@
30+
// CHECK-NEXT: Generic signature: <T where T : Q>
31+
func f3<T : Q>(_: T) where T.A : C, T.A == any (C & P2) {}
32+
// expected-error@-1 {{no type for 'T.A' can satisfy both 'T.A : C' and 'T.A == any C & P2'}}
33+
// expected-error@-2 {{no type for 'T.A' can satisfy both 'T.A : _NativeClass' and 'T.A == any C & P2'}}
34+
35+
// CHECK-LABEL: .f4@
36+
// CHECK-NEXT: Generic signature: <T where T : Q>
37+
func f4<T : Q>(_: T) where T.A : C, T.A == any (C & P2) {}
38+
// expected-error@-1 {{no type for 'T.A' can satisfy both 'T.A : C' and 'T.A == any C & P2'}}
39+
// expected-error@-2 {{no type for 'T.A' can satisfy both 'T.A : _NativeClass' and 'T.A == any C & P2'}}
40+
41+
// CHECK-LABEL: .f5@
42+
// CHECK-NEXT: Generic signature: <T where T : Q>
43+
func f5<T : Q>(_: T) where T.A : C, T.A == any (C & P3) {}
44+
// expected-error@-1 {{no type for 'T.A' can satisfy both 'T.A : C' and 'T.A == any C & P3'}}
45+
// expected-error@-2 {{no type for 'T.A' can satisfy both 'T.A : _NativeClass' and 'T.A == any C & P3'}}
46+

0 commit comments

Comments
 (0)