Skip to content

Commit 5d0fe0a

Browse files
authored
Merge pull request #78705 from jckarter/explicit-conditional-invertible-extension-requirements
Require explicit statement of all `Copyable`/`Escapable` requirements for conditional `Copyable`/`Escapable` conformances.
2 parents 2af03dd + a0a26b8 commit 5d0fe0a

18 files changed

+224
-55
lines changed

include/swift/AST/Decl.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2090,7 +2090,12 @@ class ExtensionDecl final : public GenericContext, public Decl,
20902090

20912091
/// Does this extension add conformance to an invertible protocol for the
20922092
/// extended type?
2093-
bool isAddingConformanceToInvertible() const;
2093+
///
2094+
/// Returns \c nullopt if the extension does not add conformance to any
2095+
/// invertible protocol. Returns one of the invertible protocols being
2096+
/// conformed to otherwise.
2097+
std::optional<InvertibleProtocolKind>
2098+
isAddingConformanceToInvertible() const;
20942099

20952100
/// If this extension represents an imported Objective-C category, returns the
20962101
/// category's name. Otherwise returns the empty identifier.

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7864,6 +7864,16 @@ ERROR(extension_conforms_to_invertible_and_others, none,
78647864
ERROR(invertible_conformance_other_source_file,none,
78657865
"conformance to '%0' must occur in the same source file as %kind1",
78667866
(StringRef, const ValueDecl *))
7867+
ERROR(inverse_conditional_must_be_fully_explicit,none,
7868+
"conditional conformance to %0 must explicitly "
7869+
"state whether %1 is required to conform to %2 or not",
7870+
(const ProtocolDecl *, Type, const ProtocolDecl *))
7871+
NOTE(make_inverse_conditional_non_requirement_explicit,none,
7872+
"specify that the conformance does not require %0 to conform to %1",
7873+
(Type, const ProtocolDecl *))
7874+
NOTE(make_inverse_conditional_requirement_explicit,none,
7875+
"specify that the conformance requires %0 to conform to %1",
7876+
(Type, const ProtocolDecl *))
78677877

78687878
// -- older ones below --
78697879
ERROR(noncopyable_parameter_requires_ownership, none,

include/swift/AST/TypeCheckRequests.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2118,7 +2118,8 @@ class InferredGenericSignatureRequest :
21182118
WhereClauseOwner,
21192119
SmallVector<Requirement, 2>,
21202120
SmallVector<TypeBase *, 2>,
2121-
SourceLoc, bool, bool),
2121+
SourceLoc, ExtensionDecl *,
2122+
bool),
21222123
RequestFlags::Uncached> {
21232124
public:
21242125
using SimpleRequest::SimpleRequest;
@@ -2134,7 +2135,8 @@ class InferredGenericSignatureRequest :
21342135
WhereClauseOwner whereClause,
21352136
SmallVector<Requirement, 2> addedRequirements,
21362137
SmallVector<TypeBase *, 2> inferenceSources,
2137-
SourceLoc loc, bool isExtension, bool allowInverses) const;
2138+
SourceLoc loc, ExtensionDecl *forExtension,
2139+
bool allowInverses) const;
21382140

21392141
public:
21402142
/// Inferred generic signature requests don't have source-location info.

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ SWIFT_REQUEST(TypeChecker, InferredGenericSignatureRequest,
206206
WhereClauseOwner,
207207
SmallVector<Requirement, 2>,
208208
SmallVector<TypeBase *, 2>,
209-
SourceLoc, bool, bool),
209+
SourceLoc, ExtensionDecl *, bool),
210210
Uncached, NoLocationInfo)
211211
SWIFT_REQUEST(TypeChecker, DistributedModuleIsAvailableRequest,
212212
bool(const ValueDecl *), Cached, NoLocationInfo)

lib/AST/ASTPrinter.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,7 @@ class PrintAST : public ASTVisitor<PrintAST> {
10041004
SwapSelfAndDependentMemberType = 8,
10051005
PrintInherited = 16,
10061006
PrintInverseRequirements = 32,
1007+
PrintExplicitInvertibleRequirements = 64,
10071008
};
10081009

10091010
/// The default generic signature flags for printing requirements.
@@ -1773,7 +1774,18 @@ void PrintAST::printGenericSignature(
17731774
SmallVector<Requirement, 2> requirements;
17741775
SmallVector<InverseRequirement, 2> inverses;
17751776

1776-
if (flags & PrintInverseRequirements) {
1777+
if (flags & PrintExplicitInvertibleRequirements) {
1778+
// Collect the inverted requirements…
1779+
SmallVector<Requirement, 2> filteredRequirements;
1780+
genericSig->getRequirementsWithInverses(filteredRequirements, inverses);
1781+
llvm::erase_if(inverses, [&](InverseRequirement inverse) -> bool {
1782+
return !inverseFilter(inverse);
1783+
});
1784+
// …and also all of the unfiltered requirements, including the ones for
1785+
// invertible protocols.
1786+
requirements.append(genericSig.getRequirements().begin(),
1787+
genericSig.getRequirements().end());
1788+
} else if (flags & PrintInverseRequirements) {
17771789
genericSig->getRequirementsWithInverses(requirements, inverses);
17781790
llvm::erase_if(inverses, [&](InverseRequirement inverse) -> bool {
17791791
return !inverseFilter(inverse);
@@ -3123,7 +3135,7 @@ void PrintAST::printExtension(ExtensionDecl *decl) {
31233135
// in such an extension. We need to print the whole signature:
31243136
// extension S: Copyable where T: Copyable
31253137
if (decl->isAddingConformanceToInvertible())
3126-
genSigFlags &= ~PrintInverseRequirements;
3138+
genSigFlags |= PrintExplicitInvertibleRequirements;
31273139

31283140
printGenericSignature(genericSig,
31293141
genSigFlags,

lib/AST/Decl.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,7 +1987,8 @@ Type ExtensionDecl::getExtendedType() const {
19871987
return ErrorType::get(ctx);
19881988
}
19891989

1990-
bool ExtensionDecl::isAddingConformanceToInvertible() const {
1990+
std::optional<InvertibleProtocolKind>
1991+
ExtensionDecl::isAddingConformanceToInvertible() const {
19911992
const unsigned numEntries = getInherited().size();
19921993
for (unsigned i = 0; i < numEntries; ++i) {
19931994
InheritedTypeRequest request{this, i, TypeResolutionStage::Structural};
@@ -2005,10 +2006,10 @@ bool ExtensionDecl::isAddingConformanceToInvertible() const {
20052006

20062007
if (inheritedTy)
20072008
if (auto kp = inheritedTy->getKnownProtocol())
2008-
if (getInvertibleProtocolKind(*kp))
2009-
return true;
2009+
if (auto kind = getInvertibleProtocolKind(*kp))
2010+
return kind;
20102011
}
2011-
return false;
2012+
return std::nullopt;
20122013
}
20132014

20142015
bool Decl::isObjCImplementation() const {

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -776,15 +776,15 @@ InferredGenericSignatureRequest::evaluate(
776776
WhereClauseOwner whereClause,
777777
SmallVector<Requirement, 2> addedRequirements,
778778
SmallVector<TypeBase *, 2> inferenceSources,
779-
SourceLoc loc, bool isExtension, bool allowInverses) const {
779+
SourceLoc loc, ExtensionDecl *forExtension, bool allowInverses) const {
780780
GenericSignature parentSig(parentSigImpl);
781781

782782
SmallVector<GenericTypeParamType *, 4> genericParams(
783783
parentSig.getGenericParams().begin(),
784784
parentSig.getGenericParams().end());
785785

786786
unsigned numOuterParams = genericParams.size();
787-
if (isExtension) {
787+
if (forExtension) {
788788
numOuterParams = 0;
789789
}
790790

@@ -889,6 +889,68 @@ InferredGenericSignatureRequest::evaluate(
889889
SmallVector<StructuralRequirement, 2> defaults;
890890
InverseRequirement::expandDefaults(ctx, paramTypes, defaults);
891891
applyInverses(ctx, paramTypes, inverses, defaults, errors);
892+
893+
// Any remaining implicit defaults in a conditional inverse requirement
894+
// extension must be made explicit.
895+
if (forExtension) {
896+
auto invertibleProtocol = forExtension->isAddingConformanceToInvertible();
897+
// FIXME: to workaround a reverse condfail, always infer the requirements if
898+
// the extension is in a swiftinterface file. This is temporary and should
899+
// be removed soon. (rdar://130424971)
900+
if (auto *sf = forExtension->getOutermostParentSourceFile()) {
901+
if (sf->Kind == SourceFileKind::Interface
902+
&& !ctx.LangOpts.hasFeature(Feature::SE427NoInferenceOnExtension)) {
903+
invertibleProtocol = std::nullopt;
904+
}
905+
}
906+
if (invertibleProtocol) {
907+
for (auto &def : defaults) {
908+
// Check whether a corresponding explicit requirement was provided.
909+
for (auto &req : requirements) {
910+
// An explicit requirement can match the default exactly.
911+
if (req.req.getCanonical() == def.req.getCanonical()) {
912+
goto next;
913+
}
914+
915+
// Disregard requirements on other parameters.
916+
if (!req.req.getFirstType()->isEqual(def.req.getFirstType())) {
917+
continue;
918+
}
919+
920+
// Or it can be implied by a requirement on something that's inherently
921+
// copyable.
922+
if (req.req.getKind() == RequirementKind::Superclass) {
923+
// classes are currently always escapable and copyable
924+
goto next;
925+
}
926+
if (req.req.getKind() == RequirementKind::Layout) {
927+
// layout constraints currently always imply escapable and copyable
928+
goto next;
929+
}
930+
if (req.req.getKind() == RequirementKind::Conformance
931+
&& req.req.getProtocolDecl()
932+
->inheritsFrom(def.req.getProtocolDecl())) {
933+
goto next;
934+
}
935+
936+
// A same-type constraint removes the ability for the copyability
937+
// to vary independently at all.
938+
if (req.req.getKind() == RequirementKind::SameType) {
939+
goto next;
940+
}
941+
}
942+
ctx.Diags.diagnose(loc,diag::inverse_conditional_must_be_fully_explicit,
943+
ctx.getProtocol(getKnownProtocolKind(*invertibleProtocol)),
944+
def.req.getFirstType(),
945+
def.req.getProtocolDecl());
946+
next:;
947+
}
948+
// Don't actually apply the inferred requirements since they should be
949+
// stated explicitly.
950+
defaults.clear();
951+
}
952+
}
953+
892954
requirements.append(defaults);
893955

894956
auto &rewriteCtx = ctx.getRewriteContext();
@@ -954,7 +1016,7 @@ InferredGenericSignatureRequest::evaluate(
9541016
if (attempt == 0) {
9551017
machine->computeRequirementDiagnostics(errors, inverses, loc);
9561018
diagnoseRequirementErrors(ctx, errors,
957-
(isExtension || !genericParamList)
1019+
(forExtension || !genericParamList)
9581020
? AllowConcreteTypePolicy::All
9591021
: AllowConcreteTypePolicy::AssocTypes);
9601022
}
@@ -985,7 +1047,7 @@ InferredGenericSignatureRequest::evaluate(
9851047
std::move(machine));
9861048
}
9871049

988-
if (genericParamList && !isExtension) {
1050+
if (genericParamList && !forExtension) {
9891051
for (auto genericParam : result.getInnermostGenericParams()) {
9901052
auto reduced = result.getReducedType(genericParam);
9911053

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3564,7 +3564,7 @@ SerializeAttrGenericSignatureRequest::evaluate(Evaluator &evaluator,
35643564
/*addedRequirements=*/{},
35653565
/*inferenceSources=*/{},
35663566
attr->getLocation(),
3567-
/*isExtension=*/false,
3567+
/*forExtension=*/nullptr,
35683568
/*allowInverses=*/true};
35693569

35703570
auto specializedSig = evaluateOrDefault(Ctx.evaluator, request,
@@ -6161,7 +6161,7 @@ bool resolveDifferentiableAttrDerivativeGenericSignature(
61616161
/*addedRequirements=*/{},
61626162
/*inferenceSources=*/{},
61636163
attr->getLocation(),
6164-
/*isExtension=*/false,
6164+
/*forExtension=*/nullptr,
61656165
/*allowInverses=*/true};
61666166

61676167
// Compute generic signature for derivative functions.

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator,
119119
/*addedRequirements=*/{},
120120
/*inferenceSources=*/{},
121121
repr->getLoc(),
122-
/*isExtension=*/false,
122+
/*forExtension=*/nullptr,
123123
/*allowInverses=*/true};
124124

125125
interfaceSignature = evaluateOrDefault(
@@ -689,7 +689,6 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
689689
SmallVector<TypeBase *, 2> inferenceSources;
690690
SmallVector<Requirement, 2> extraReqs;
691691
SourceLoc loc;
692-
bool inferInvertibleReqs = true;
693692

694693
if (auto VD = dyn_cast<ValueDecl>(GC->getAsDecl())) {
695694
loc = VD->getLoc();
@@ -787,20 +786,6 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
787786
} else if (auto *ext = dyn_cast<ExtensionDecl>(GC)) {
788787
loc = ext->getLoc();
789788

790-
// If the extension introduces conformance to invertible protocol IP, do not
791-
// infer any conditional requirements that the generic parameters to conform
792-
// to invertible protocols. This forces people to write out the conditions.
793-
inferInvertibleReqs = !ext->isAddingConformanceToInvertible();
794-
795-
// FIXME: to workaround a reverse condfail, always infer the requirements if
796-
// the extension is in a swiftinterface file. This is temporary and should
797-
// be removed soon. (rdar://130424971)
798-
if (auto *sf = ext->getOutermostParentSourceFile()) {
799-
if (sf->Kind == SourceFileKind::Interface
800-
&& !ctx.LangOpts.hasFeature(Feature::SE427NoInferenceOnExtension))
801-
inferInvertibleReqs = true;
802-
}
803-
804789
collectAdditionalExtensionRequirements(ext->getExtendedType(), extraReqs);
805790

806791
auto *extendedNominal = ext->getExtendedNominal();
@@ -838,8 +823,8 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
838823
parentSig.getPointer(),
839824
genericParams, WhereClauseOwner(GC),
840825
extraReqs, inferenceSources, loc,
841-
/*isExtension=*/isa<ExtensionDecl>(GC),
842-
/*allowInverses=*/inferInvertibleReqs};
826+
/*forExtension=*/dyn_cast<ExtensionDecl>(GC),
827+
/*allowInverses=*/true};
843828
return evaluateOrDefault(ctx.evaluator, request,
844829
GenericSignatureWithError()).getPointer();
845830
}

lib/Sema/TypeChecker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ swift::handleSILGenericParams(GenericParamList *genericParams,
516516
/*parentSig=*/nullptr,
517517
nestedList.back(), WhereClauseOwner(),
518518
{}, {}, genericParams->getLAngleLoc(),
519-
/*isExtension=*/false,
519+
/*forExtension=*/nullptr,
520520
allowInverses};
521521
return evaluateOrDefault(DC->getASTContext().evaluator, request,
522522
GenericSignatureWithError()).getPointer();
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s
2+
3+
struct C_C1<T: ~Copyable>: ~Copyable {}
4+
extension C_C1: Copyable where T: Copyable {}
5+
6+
struct C_C2<T: ~Copyable>: ~Copyable {}
7+
// expected-error @+2 {{conditional conformance to 'Copyable' must explicitly state whether 'T' is required to conform to 'Copyable'}}
8+
// expected-error @+1 {{marked with '~Copyable'}}
9+
extension C_C2: Copyable {}
10+
11+
struct C_CE1<T: ~Copyable & ~Escapable>: ~Copyable {}
12+
extension C_CE1: Copyable where T: Copyable, T: ~Escapable {}
13+
14+
struct C_CE2<T: ~Copyable & ~Escapable>: ~Copyable {}
15+
// expected-error @+1 {{conditional conformance to 'Copyable' must explicitly state whether 'T' is required to conform to 'Escapable'}}
16+
extension C_CE2: Copyable where T: Copyable {}
17+
18+
struct C_CE3<T: ~Copyable & ~Escapable>: ~Copyable {}
19+
// expected-error @+2 {{conditional conformance to 'Copyable' must explicitly state whether 'T' is required to conform to 'Copyable'}}
20+
// expected-error @+1 {{marked with '~Copyable'}}
21+
extension C_CE3: Copyable where T: ~Escapable {}
22+
23+
struct CE_C<T: ~Copyable>: ~Copyable, ~Escapable {}
24+
extension CE_C: Copyable where T: Copyable {}
25+
26+
struct CE_E<T: ~Escapable>: ~Copyable, ~Escapable {}
27+
extension CE_E: Escapable where T: Escapable {}
28+
29+
struct CE_CE1<T: ~Copyable & ~Escapable>: ~Copyable, ~Escapable {}
30+
extension CE_CE1: Copyable where T: Copyable, T: ~Escapable {}
31+
extension CE_CE1: Escapable where T: ~Copyable, T: Escapable {}
32+
33+
struct CE_CE2<T: ~Copyable & ~Escapable>: ~Copyable, ~Escapable {}
34+
// expected-error @+1 {{conditional conformance to 'Copyable' must explicitly state whether 'T' is required to conform to 'Escapable'}}
35+
extension CE_CE2: Copyable where T: Copyable {}
36+
// expected-error @+1 {{conditional conformance to 'Escapable' must explicitly state whether 'T' is required to conform to 'Copyable'}}
37+
extension CE_CE2: Escapable where T: Escapable {}
38+
39+
struct CE_CE3<T: ~Copyable & ~Escapable>: ~Copyable, ~Escapable {}
40+
// expected-error @+2 {{conditional conformance to 'Copyable' must explicitly state whether 'T' is required to conform to 'Copyable'}}
41+
// expected-error @+1 {{marked with '~Copyable'}}
42+
extension CE_CE3: Copyable where T: ~Escapable {}
43+
// expected-error @+2 {{conditional conformance to 'Escapable' must explicitly state whether 'T' is required to conform to 'Escapable'}}
44+
// expected-error @+1 {{marked with '~Escapable'}}
45+
extension CE_CE3: Escapable where T: ~Copyable {}
46+
47+
struct C_C_X<T: ~Copyable, U>: ~Copyable {}
48+
extension C_C_X: Copyable where T: Copyable {}
49+
50+
protocol Floppyable {} // implied Copyable requirement
51+
52+
struct C_C_P<T: ~Copyable, U: Floppyable>: ~Copyable {}
53+
extension C_C_P: Copyable where T: Copyable {}
54+
55+
class Sloppyable {} // implied Copyable requirement since it's a class
56+
57+
struct C_C_K<T: ~Copyable, U: Sloppyable>: ~Copyable {}
58+
extension C_C_K: Copyable where T: Copyable {}
59+
60+
protocol Ploppyable: ~Copyable {}
61+
62+
struct C_C_PC1<T: ~Copyable, U: Ploppyable & ~Copyable>: ~Copyable {}
63+
// expected-error @+1 {{conditional conformance to 'Copyable' must explicitly state whether 'U' is required to conform to 'Copyable'}}
64+
extension C_C_PC1: Copyable where T: Copyable {}
65+
66+
struct C_C_PC2<T: ~Copyable, U: Ploppyable & ~Copyable>: ~Copyable {}
67+
extension C_C_PC2: Copyable where T: Copyable, U: Copyable {}
68+
69+
struct C_C_X2<T: ~Copyable, U: Ploppyable /*& Copyable*/>: ~Copyable {}
70+
extension C_C_X2: Copyable where T: Copyable {}
71+
72+
struct C_C_L<T: ~Copyable, U: AnyObject>: ~Copyable {}
73+
extension C_C_L: Copyable where T: Copyable {}
74+
75+
struct C_C_C<T: ~Copyable, U: ~Copyable> {}
76+
extension C_C_C where T: ~Copyable, U == Int {
77+
struct SubInt: ~Copyable {}
78+
}
79+
80+
extension C_C_C.SubInt: Copyable where T: Copyable {}
81+
82+
struct NC: ~Copyable {}
83+
84+
extension C_C_C where T: ~Copyable, U: ~Copyable, U == NC {
85+
struct SubNC: ~Copyable {}
86+
}
87+
88+
extension C_C_C.SubNC: Copyable where T: Copyable {}

0 commit comments

Comments
 (0)