Skip to content

Commit ec4a125

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.
1 parent 43ce060 commit ec4a125

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
@@ -2915,6 +2915,18 @@ void PrintAST::printExtendedTypeName(TypeLoc ExtendedTypeLoc) {
29152915
printTypeLoc(TypeLoc(ExtendedTypeLoc.getTypeRepr(), Ty));
29162916
}
29172917

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

29192931
void PrintAST::printSynthesizedExtension(Type ExtendedType,
29202932
ExtensionDecl *ExtDecl) {
@@ -3039,9 +3051,21 @@ void PrintAST::printExtension(ExtensionDecl *decl) {
30393051
auto baseGenericSig = decl->getExtendedNominal()->getGenericSignature();
30403052
assert(baseGenericSig &&
30413053
"an extension can't be generic if the base type isn't");
3054+
3055+
auto genSigFlags = defaultGenericRequirementFlags()
3056+
| IncludeOuterInverses;
3057+
3058+
// Disable printing inverses if the extension is adding a conformance
3059+
// for an invertible protocol itself, as we do not infer any requirements
3060+
// in such an extension. We need to print the whole signature:
3061+
// extension S: Copyable where T: Copyable
3062+
if (isExtensionAddingInvertibleConformance(decl)) {
3063+
genSigFlags &= ~PrintInverseRequirements;
3064+
genSigFlags &= ~IncludeOuterInverses;
3065+
}
3066+
30423067
printGenericSignature(genericSig,
3043-
defaultGenericRequirementFlags()
3044-
| IncludeOuterInverses,
3068+
genSigFlags,
30453069
[baseGenericSig](const Requirement &req) -> bool {
30463070
// Only include constraints that are not satisfied by the base type.
30473071
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
@@ -684,6 +684,7 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
684684
SmallVector<TypeBase *, 2> inferenceSources;
685685
SmallVector<Requirement, 2> extraReqs;
686686
SourceLoc loc;
687+
bool inferInvertibleReqs = true;
687688

688689
if (auto VD = dyn_cast<ValueDecl>(GC->getAsDecl())) {
689690
loc = VD->getLoc();
@@ -781,6 +782,35 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
781782
} else if (auto *ext = dyn_cast<ExtensionDecl>(GC)) {
782783
loc = ext->getLoc();
783784

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

786816
auto *extendedNominal = ext->getExtendedNominal();
@@ -800,7 +830,7 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
800830
genericParams, WhereClauseOwner(GC),
801831
extraReqs, inferenceSources, loc,
802832
/*isExtension=*/isa<ExtensionDecl>(GC),
803-
/*allowInverses=*/true};
833+
/*allowInverses=*/inferInvertibleReqs};
804834
return evaluateOrDefault(ctx.evaluator, request,
805835
GenericSignatureWithError()).getPointer();
806836
}

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)