Skip to content

Commit 9c9bfdd

Browse files
committed
NCGenerics: ext's might not infer invertible req's
If the extension adds conformance to an invertible protocol, it's confusing for people to also infer conditional requirements on the generic parameters for those invertible protocols. This came up in the review of SE-427. (cherry picked from commit ec4a125)
1 parent 0e9396b commit 9c9bfdd

20 files changed

+123
-52
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2913,6 +2913,18 @@ void PrintAST::printExtendedTypeName(TypeLoc ExtendedTypeLoc) {
29132913
printTypeLoc(TypeLoc(ExtendedTypeLoc.getTypeRepr(), Ty));
29142914
}
29152915

2916+
/// If an extension adds a conformance for an invertible protocol, then we
2917+
/// should not print inverses for its generic signature, because no conditional
2918+
/// requirements are inferred by default for such an extension.
2919+
static bool isExtensionAddingInvertibleConformance(const ExtensionDecl *ext) {
2920+
auto conformances = ext->getLocalConformances();
2921+
for (auto *conf : conformances) {
2922+
if (conf->getProtocol()->getInvertibleProtocolKind()) {
2923+
return true;
2924+
}
2925+
}
2926+
return false;
2927+
}
29162928

29172929
void PrintAST::printSynthesizedExtension(Type ExtendedType,
29182930
ExtensionDecl *ExtDecl) {
@@ -3037,9 +3049,21 @@ void PrintAST::printExtension(ExtensionDecl *decl) {
30373049
auto baseGenericSig = decl->getExtendedNominal()->getGenericSignature();
30383050
assert(baseGenericSig &&
30393051
"an extension can't be generic if the base type isn't");
3052+
3053+
auto genSigFlags = defaultGenericRequirementFlags()
3054+
| IncludeOuterInverses;
3055+
3056+
// Disable printing inverses if the extension is adding a conformance
3057+
// for an invertible protocol itself, as we do not infer any requirements
3058+
// in such an extension. We need to print the whole signature:
3059+
// extension S: Copyable where T: Copyable
3060+
if (isExtensionAddingInvertibleConformance(decl)) {
3061+
genSigFlags &= ~PrintInverseRequirements;
3062+
genSigFlags &= ~IncludeOuterInverses;
3063+
}
3064+
30403065
printGenericSignature(genericSig,
3041-
defaultGenericRequirementFlags()
3042-
| IncludeOuterInverses,
3066+
genSigFlags,
30433067
[baseGenericSig](const Requirement &req) -> bool {
30443068
// Only include constraints that are not satisfied by the base type.
30453069
return !baseGenericSig->isRequirementSatisfied(req);

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,6 @@ InferredGenericSignatureRequest::evaluate(
782782

783783
unsigned numOuterParams = genericParams.size();
784784
if (isExtension) {
785-
assert(allowInverses);
786785
numOuterParams = 0;
787786
}
788787

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
687687
SmallVector<TypeBase *, 2> inferenceSources;
688688
SmallVector<Requirement, 2> extraReqs;
689689
SourceLoc loc;
690+
bool inferInvertibleReqs = true;
690691

691692
if (auto VD = dyn_cast<ValueDecl>(GC->getAsDecl())) {
692693
loc = VD->getLoc();
@@ -784,6 +785,35 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
784785
} else if (auto *ext = dyn_cast<ExtensionDecl>(GC)) {
785786
loc = ext->getLoc();
786787

788+
// The inherited entries influence the generic signature of the extension,
789+
// because if it introduces conformance to invertible protocol IP, we do not
790+
// we do not infer any requirements that the generic parameters to conform
791+
// to invertible protocols. This forces people to write out the conditions.
792+
const unsigned numEntries = ext->getInherited().size();
793+
for (unsigned i = 0; i < numEntries; ++i) {
794+
InheritedTypeRequest request{ext, i, TypeResolutionStage::Structural};
795+
auto result = evaluateOrDefault(ctx.evaluator, request,
796+
InheritedTypeResult::forDefault());
797+
Type inheritedTy;
798+
switch (result) {
799+
case InheritedTypeResult::Inherited:
800+
inheritedTy = result.getInheritedType();
801+
break;
802+
case InheritedTypeResult::Suppressed:
803+
case InheritedTypeResult::Default:
804+
continue;
805+
}
806+
807+
if (inheritedTy) {
808+
if (auto kp = inheritedTy->getKnownProtocol()) {
809+
if (getInvertibleProtocolKind(*kp)) {
810+
inferInvertibleReqs = false;
811+
break;
812+
}
813+
}
814+
}
815+
}
816+
787817
collectAdditionalExtensionRequirements(ext->getExtendedType(), extraReqs);
788818

789819
auto *extendedNominal = ext->getExtendedNominal();
@@ -815,7 +845,7 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
815845
genericParams, WhereClauseOwner(GC),
816846
extraReqs, inferenceSources, loc,
817847
/*isExtension=*/isa<ExtensionDecl>(GC),
818-
/*allowInverses=*/true};
848+
/*allowInverses=*/inferInvertibleReqs};
819849
return evaluateOrDefault(ctx.evaluator, request,
820850
GenericSignatureWithError()).getPointer();
821851
}

stdlib/public/core/Optional.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public enum Optional<Wrapped: ~Copyable>: ~Copyable {
132132
case some(Wrapped)
133133
}
134134

135-
extension Optional: Copyable /* where Wrapped: Copyable */ {}
135+
extension Optional: Copyable where Wrapped: Copyable {}
136136

137137
extension Optional: Sendable where Wrapped: ~Copyable & Sendable { }
138138

stdlib/public/core/Result.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public enum Result<Success: ~Copyable, Failure: Error> {
2121
case failure(Failure)
2222
}
2323

24-
extension Result: Copyable /* where Success: Copyable */ {}
24+
extension Result: Copyable where Success: Copyable {}
2525

2626
extension Result: Sendable where Success: Sendable & ~Copyable {}
2727

test/Generics/inverse_conditional_conformance_restriction.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ protocol Q {}
77
class DoggoClass {}
88

99
struct Blah<T: ~Copyable & ~Escapable>: ~Copyable, ~Escapable {}
10-
extension Blah: Copyable {} // expected-error {{conditional conformance to suppressible protocol 'Copyable' cannot depend on 'T: Escapable'}}
11-
extension Blah: Escapable {} // expected-error {{conditional conformance to suppressible protocol 'Escapable' cannot depend on 'T: Copyable'}}
10+
extension Blah: Copyable where T: Copyable & Escapable {}
11+
// expected-error@-1 {{conditional conformance to suppressible protocol 'Copyable' cannot depend on 'T: Escapable'}}
1212

13-
struct Yuck<T: ~Copyable & ~Escapable>: ~Copyable, ~Escapable {}
14-
extension Yuck: Copyable where T: ~Escapable {}
15-
extension Yuck: Escapable where T: ~Copyable {}
13+
extension Blah: Escapable where T: Copyable, T: Escapable {}
14+
// expected-error@-1 {{conditional conformance to suppressible protocol 'Escapable' cannot depend on 'T: Copyable'}}
15+
16+
struct Fixed<T: ~Copyable & ~Escapable>: ~Copyable, ~Escapable {}
17+
extension Fixed: Copyable where T: Copyable {}
18+
extension Fixed: Escapable where T: Escapable {}
1619

1720
struct TryConformance<Whatever: ~Copyable>: ~Copyable {}
1821
extension TryConformance: Copyable

test/Generics/inverse_generics.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,26 @@
22
// RUN: -enable-experimental-feature NonescapableTypes \
33
// RUN: -enable-experimental-feature SuppressedAssociatedTypes
44

5+
// expected-note@+1 {{'T' has '~Copyable' constraint preventing implicit 'Copyable' conformance}}
6+
struct AttemptImplicitConditionalConformance<T: ~Copyable>: ~Copyable {
7+
var t: T // expected-error {{stored property 't' of 'Copyable'-conforming generic struct 'AttemptImplicitConditionalConformance' has non-Copyable type 'T'}}
8+
}
9+
extension AttemptImplicitConditionalConformance: Copyable {}
10+
// expected-error@-1 {{generic struct 'AttemptImplicitConditionalConformance' required to be 'Copyable' but is marked with '~Copyable'}}
11+
12+
enum Hello<T: ~Escapable & ~Copyable>: ~Escapable & ~Copyable {}
13+
extension Hello: Escapable {} // expected-error {{generic enum 'Hello' required to be 'Escapable' but is marked with '~Escapable'}}
14+
extension Hello: Copyable {} // expected-error {{generic enum 'Hello' required to be 'Copyable' but is marked with '~Copyable'}}
15+
16+
enum HelloExplicitlyFixed<T: ~Escapable & ~Copyable>: Escapable, Copyable {}
517

18+
struct NoInverseBecauseNoDefault<T: ~Copyable & ~Escapable>: ~Copyable {}
19+
extension NoInverseBecauseNoDefault: Copyable where T: Copyable, T: ~Escapable {}
20+
// expected-error@-1 {{cannot suppress '~Escapable' on generic parameter 'T' defined in outer scope}}
621

722
// Check support for explicit conditional conformance
823
public struct ExplicitCond<T: ~Copyable>: ~Copyable {}
9-
extension ExplicitCond: Copyable {}
24+
extension ExplicitCond: Copyable where T: Copyable {}
1025
// expected-note@-1 {{requirement from conditional conformance}}
1126
// expected-note@-2 {{requirement from conditional conformance of 'ExplicitCondAlias<NC>' (aka 'ExplicitCond<NC>') to 'Copyable'}}
1227

@@ -80,7 +95,7 @@ struct ConditionalContainment<T: ~Copyable>: ~Copyable {
8095
var y: NC // expected-error {{stored property 'y' of 'Copyable'-conforming generic struct 'ConditionalContainment' has non-Copyable type 'NC'}}
8196
}
8297

83-
extension ConditionalContainment: Copyable {}
98+
extension ConditionalContainment: Copyable where T: Copyable {}
8499

85100
func chk(_ T: RequireCopyable<ConditionalContainment<Int>>) {}
86101

@@ -126,7 +141,7 @@ enum Maybe<Wrapped: ~Copyable>: ~Copyable {
126141
deinit {} // expected-error {{deinitializer cannot be declared in generic enum 'Maybe' that conforms to 'Copyable'}}
127142
}
128143

129-
extension Maybe: Copyable {}
144+
extension Maybe: Copyable where Wrapped: Copyable {}
130145

131146
// expected-note@+4{{requirement specified as 'NC' : 'Copyable'}}
132147
// expected-note@+3{{requirement from conditional conformance of 'Maybe<NC>' to 'Copyable'}}
@@ -265,7 +280,7 @@ enum MaybeEscapes<T: ~Escapable>: ~Escapable { // expected-note {{generic enum '
265280
case none
266281
}
267282

268-
extension MaybeEscapes: Escapable {}
283+
extension MaybeEscapes: Escapable where T: Escapable {}
269284

270285
struct Escapes { // expected-note {{consider adding '~Escapable' to struct 'Escapes'}}
271286
let t: MaybeEscapes<NonescapingType> // expected-error {{stored property 't' of 'Escapable'-conforming struct 'Escapes' has non-Escapable type 'MaybeEscapes<NonescapingType>'}}

test/Generics/inverse_generics_stdlib.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public enum Optional<T: ~Copyable>: ~Copyable {
2121
case none
2222
}
2323

24-
extension Optional: Copyable {}
24+
extension Optional: Copyable where T: Copyable {}
2525

2626
public func wrapping<T: ~Copyable>(_ t: consuming T) -> T? {
2727
return .some(t)

test/Inputs/Swiftskell.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public enum Either<Success: ~Copyable, Failure: Error>: ~Copyable {
3636
case failure(Failure)
3737
}
3838

39-
extension Either: Copyable {}
39+
extension Either: Copyable where Success: Copyable {}
4040

4141
extension Either where Failure == Swift.Error {
4242
public init(catching body: () throws -> Success) {
@@ -77,7 +77,7 @@ public enum Maybe<Wrapped: ~Copyable>: ~Copyable {
7777
case just(Wrapped)
7878
case nothing
7979
}
80-
extension Maybe: Copyable {}
80+
extension Maybe: Copyable where Wrapped: Copyable {}
8181

8282
extension Maybe: Show where Wrapped: Show & ~Copyable {
8383
public borrowing func show() -> String {

test/Interpreter/moveonly_generics_casting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ enum Maybe<Wrapped: ~Copyable>: ~Copyable {
136136
case just(Wrapped)
137137
case nothing
138138
}
139-
extension Maybe: Copyable {}
139+
extension Maybe: Copyable where Wrapped: Copyable {}
140140
extension Maybe: CustomDebugStringConvertible {
141141
var debugDescription: String {
142142
"cast succeeded"

0 commit comments

Comments
 (0)