Skip to content

Commit ac465ea

Browse files
authored
Merge pull request #70548 from kavon/ncgenerics-stdlib-building-v3
[NCGenerics] Even more fixes for building the stdlib (Part 3)
2 parents 3bdc2d2 + 5e050e6 commit ac465ea

37 files changed

+494
-100
lines changed

include/swift/AST/Decl.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5171,6 +5171,10 @@ class ProtocolDecl final : public NominalTypeDecl {
51715171
/// Determine whether this protocol inherits from the given ("super")
51725172
/// protocol.
51735173
bool inheritsFrom(const ProtocolDecl *Super) const;
5174+
5175+
/// Determine whether this protocol requires conformance to `IP`, without
5176+
/// querying a generic signature.
5177+
bool requiresInvertible(InvertibleProtocolKind ip) const;
51745178

51755179
SourceLoc getStartLoc() const { return ProtocolLoc; }
51765180
SourceRange getSourceRange() const {
@@ -5216,9 +5220,9 @@ class ProtocolDecl final : public NominalTypeDecl {
52165220
/// semantics but has no corresponding witness table.
52175221
bool isMarkerProtocol() const;
52185222

5219-
/// Determine whether this is an invertible protocol,
5220-
/// i.e., for a protocol P, the inverse constraint ~P exists.
5221-
bool isInvertibleProtocol() const;
5223+
/// Determine if this is an invertible protocol and return its kind,
5224+
/// i.e., for a protocol P, returns the kind if inverse constraint ~P exists.
5225+
llvm::Optional<InvertibleProtocolKind> getInvertibleProtocolKind() const;
52225226

52235227
private:
52245228
void computeKnownProtocolKind() const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7586,6 +7586,10 @@ WARNING(redundant_inverse_constraint,none,
75867586
ERROR(inverse_on_class, none,
75877587
"classes cannot be '~%0'",
75887588
(StringRef))
7589+
ERROR(inverse_with_class_constraint, none,
7590+
"composition involving %select{class requirement %2|'AnyObject'}0 "
7591+
"cannot contain '~%1'",
7592+
(bool, StringRef, Type))
75897593
ERROR(inverse_extension, none,
75907594
"cannot apply inverse %0 to extension",
75917595
(Type))

include/swift/AST/Expr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3393,6 +3393,10 @@ class ErasureExpr final : public ImplicitConversionExpr,
33933393
Bits.ErasureExpr.NumArgumentConversions = argConversions.size();
33943394
std::uninitialized_copy(argConversions.begin(), argConversions.end(),
33953395
getTrailingObjects<ConversionPair>());
3396+
3397+
assert(llvm::all_of(conformances, [](ProtocolConformanceRef ref) {
3398+
return !ref.isInvalid();
3399+
}));
33963400
}
33973401

33983402
public:

include/swift/AST/Module.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,13 @@ class ModuleDecl
853853
ProtocolConformanceRef conformsToProtocol(Type sourceTy,
854854
ProtocolDecl *targetProtocol);
855855

856+
/// Collect the conformances of \c fromType each of the protocols of an
857+
/// existential type's layout.
858+
ArrayRef<ProtocolConformanceRef>
859+
collectExistentialConformances(CanType fromType, CanType existential,
860+
bool skipConditionalRequirements = true,
861+
bool allowMissing = false);
862+
856863
/// Find a member named \p name in \p container that was declared in this
857864
/// module.
858865
///

include/swift/AST/ProtocolConformanceRef.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class ProtocolConformanceRef {
128128
/// Determine whether this conformance (or a conformance it depends on)
129129
/// involves a "missing" conformance anywhere. Such conformances
130130
/// cannot be depended on to always exist.
131-
bool hasMissingConformance(ModuleDecl *module) const;
131+
bool hasMissingConformance() const;
132132

133133
/// Enumerate the missing conformances in this conformance.
134134
///
@@ -140,7 +140,6 @@ class ProtocolConformanceRef {
140140
/// \returns \c true if any invocation of \c fn returned true,
141141
/// \c false otherwise.
142142
bool forEachMissingConformance(
143-
ModuleDecl *module,
144143
llvm::function_ref<bool(BuiltinProtocolConformance *missing)> fn) const;
145144

146145
using OpaqueValue = void*;

include/swift/AST/TypeCheckRequests.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,34 @@ class ExistentialConformsToSelfRequest :
323323
void cacheResult(bool value) const;
324324
};
325325

326+
class CollectExistentialConformancesRequest
327+
: public SimpleRequest<CollectExistentialConformancesRequest,
328+
ArrayRef<ProtocolConformanceRef>(ModuleDecl*,
329+
CanType,
330+
CanType,
331+
bool,
332+
bool),
333+
RequestFlags::Uncached> { // TODO: maybe cache this?
334+
public:
335+
using SimpleRequest::SimpleRequest;
336+
337+
private:
338+
friend SimpleRequest;
339+
340+
// Evaluation.
341+
ArrayRef<ProtocolConformanceRef>
342+
evaluate(Evaluator &evaluator,
343+
ModuleDecl *module,
344+
CanType fromType,
345+
CanType existential,
346+
bool skipConditionalRequirements,
347+
bool allowMissing) const;
348+
349+
public:
350+
// Caching.
351+
bool isCached() const { return true; }
352+
};
353+
326354
/// Determine whether an existential type conforming to this protocol
327355
/// requires the \c any syntax.
328356
class HasSelfOrAssociatedTypeRequirementsRequest :

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest,
100100
Type(EnumDecl *), Cached, NoLocationInfo)
101101
SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest,
102102
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
103+
SWIFT_REQUEST(TypeChecker, CollectExistentialConformancesRequest,
104+
ArrayRef<ProtocolConformanceRef>(ModuleDecl*,
105+
CanType,
106+
CanType,
107+
bool,
108+
bool),
109+
Uncached, NoLocationInfo)
103110
SWIFT_REQUEST(TypeChecker, HasSelfOrAssociatedTypeRequirementsRequest,
104111
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
105112
SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached,

include/swift/AST/Types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5873,6 +5873,7 @@ class ProtocolCompositionType final : public TypeBase,
58735873
private llvm::TrailingObjects<ProtocolCompositionType, Type> {
58745874
friend TrailingObjects;
58755875

5876+
// The inverse constraints `& ~IP` that are part of this composition.
58765877
InvertibleProtocolSet Inverses;
58775878

58785879
public:

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,6 +1470,10 @@ static void reconstituteInverses(GenericSignature genericSig,
14701470
for (auto tp : typeParams) {
14711471
assert(tp);
14721472

1473+
// Any generic parameter with a superclass bound could not have an inverse.
1474+
if (genericSig->getSuperclassBound(tp))
1475+
continue;
1476+
14731477
auto defaults = InverseRequirement::expandDefault(tp);
14741478
for (auto ip : defaults) {
14751479
auto *proto = ctx.getProtocol(getKnownProtocolKind(ip));

lib/AST/Decl.cpp

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6414,14 +6414,12 @@ bool ProtocolDecl::isMarkerProtocol() const {
64146414
return getAttrs().hasAttribute<MarkerAttr>();
64156415
}
64166416

6417-
bool ProtocolDecl::isInvertibleProtocol() const {
6418-
if (auto kp = getKnownProtocolKind()) {
6419-
if (getInvertibleProtocolKind(*kp)) {
6420-
assert(isMarkerProtocol());
6421-
return true;
6422-
}
6423-
}
6424-
return false;
6417+
llvm::Optional<InvertibleProtocolKind>
6418+
ProtocolDecl::getInvertibleProtocolKind() const {
6419+
if (auto kp = getKnownProtocolKind())
6420+
return ::getInvertibleProtocolKind(*kp);
6421+
6422+
return llvm::None;
64256423
}
64266424

64276425
ArrayRef<ProtocolDecl *> ProtocolDecl::getInheritedProtocols() const {
@@ -6560,9 +6558,14 @@ bool ProtocolDecl::walkInheritedProtocols(
65606558
}
65616559

65626560
bool ProtocolDecl::inheritsFrom(const ProtocolDecl *super) const {
6561+
assert(super);
6562+
65636563
if (this == super)
65646564
return false;
65656565

6566+
if (auto ip = super->getInvertibleProtocolKind())
6567+
return requiresInvertible(*ip);
6568+
65666569
return walkInheritedProtocols([super](ProtocolDecl *inherited) {
65676570
if (inherited == super)
65686571
return TypeWalker::Action::Stop;
@@ -6571,6 +6574,30 @@ bool ProtocolDecl::inheritsFrom(const ProtocolDecl *super) const {
65716574
});
65726575
}
65736576

6577+
bool ProtocolDecl::requiresInvertible(InvertibleProtocolKind ip) const {
6578+
// HACK: until we enable Feature::NoncopyableGenerics in the stdlib,
6579+
// hardcode the fact that an invertible protocol does not require any other!
6580+
if (getInvertibleProtocolKind())
6581+
return false;
6582+
6583+
auto kp = ::getKnownProtocolKind(ip);
6584+
return walkInheritedProtocols([kp, ip](ProtocolDecl *proto) {
6585+
if (proto->isSpecificProtocol(kp))
6586+
return TypeWalker::Action::Stop; // it is required.
6587+
6588+
switch (proto->getMarking(ip).getInverse().getKind()) {
6589+
case InverseMarking::Kind::None:
6590+
return TypeWalker::Action::Stop; // it is required.
6591+
6592+
case InverseMarking::Kind::LegacyExplicit:
6593+
case InverseMarking::Kind::Explicit:
6594+
case InverseMarking::Kind::Inferred:
6595+
// the implicit requirement was suppressed on this protocol, keep looking.
6596+
return TypeWalker::Action::Continue;
6597+
}
6598+
});
6599+
}
6600+
65746601
bool ProtocolDecl::requiresClass() const {
65756602
return evaluateOrDefault(getASTContext().evaluator,
65766603
ProtocolRequiresClassRequest{const_cast<ProtocolDecl *>(this)}, false);

lib/AST/Module.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,19 @@ void ModuleDecl::getDisplayDecls(SmallVectorImpl<Decl*> &Results, bool Recursive
15681568
#endif
15691569
}
15701570

1571+
ArrayRef<ProtocolConformanceRef>
1572+
ModuleDecl::collectExistentialConformances(CanType fromType,
1573+
CanType existential,
1574+
bool skipConditionalRequirements,
1575+
bool allowMissing) {
1576+
CollectExistentialConformancesRequest request{this,
1577+
fromType,
1578+
existential,
1579+
skipConditionalRequirements,
1580+
allowMissing};
1581+
return evaluateOrDefault(getASTContext().evaluator, request, /*default=*/{});
1582+
}
1583+
15711584
ProtocolConformanceRef
15721585
ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
15731586
ASTContext &ctx = getASTContext();
@@ -1696,7 +1709,7 @@ ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
16961709
// replace the result with an "invalid" result.
16971710
if (!allowMissing &&
16981711
shouldCreateMissingConformances(type, protocol) &&
1699-
result.hasMissingConformance(this))
1712+
result.hasMissingConformance())
17001713
return ProtocolConformanceRef::forInvalid();
17011714

17021715
return result;

lib/AST/ProtocolConformanceRef.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ ProtocolConformanceRef::subst(Type origType, InFlightSubstitution &IFS) const {
118118
return ProtocolConformanceRef::forInvalid();
119119
}
120120

121+
// If the type has been fully substituted and the requirement is for
122+
// an invertible protocol, just do a module lookup. This avoids an infinite
123+
// substitution issue by recognizing that these protocols are very simple
124+
// (see rdar://119950540 for the general issue).
125+
if (!substType->hasTypeParameter() && proto->getInvertibleProtocolKind())
126+
return proto->getModuleContext()->lookupConformance(substType, proto);
127+
121128
// Check the conformance map.
122129
// FIXME: Pack element level?
123130
return IFS.lookupConformance(origType->getCanonicalType(), substType, proto,
@@ -312,22 +319,21 @@ bool ProtocolConformanceRef::hasUnavailableConformance() const {
312319
return false;
313320
}
314321

315-
bool ProtocolConformanceRef::hasMissingConformance(ModuleDecl *module) const {
316-
return forEachMissingConformance(module,
322+
bool ProtocolConformanceRef::hasMissingConformance() const {
323+
return forEachMissingConformance(
317324
[](BuiltinProtocolConformance *builtin) {
318325
return true;
319326
});
320327
}
321328

322329
bool ProtocolConformanceRef::forEachMissingConformance(
323-
ModuleDecl *module,
324330
llvm::function_ref<bool(BuiltinProtocolConformance *missing)> fn) const {
325331
if (isInvalid() || isAbstract())
326332
return false;
327333

328334
if (isPack()) {
329335
for (auto conformance : getPack()->getPatternConformances()) {
330-
if (conformance.forEachMissingConformance(module, fn))
336+
if (conformance.forEachMissingConformance(fn))
331337
return true;
332338
}
333339

@@ -345,7 +351,7 @@ bool ProtocolConformanceRef::forEachMissingConformance(
345351
// Check conformances that are part of this conformance.
346352
auto subMap = concreteConf->getSubstitutionMap();
347353
for (auto conformance : subMap.getConformances()) {
348-
if (conformance.forEachMissingConformance(module, fn))
354+
if (conformance.forEachMissingConformance(fn))
349355
return true;
350356
}
351357

lib/AST/RequirementMachine/Diagnostics.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,11 +564,23 @@ void RequirementMachine::computeRequirementDiagnostics(
564564
getGenericParams());
565565

566566
// Check that the generic parameters with inverses truly lack the conformance.
567-
for (auto const& inverse : inverses)
568-
if (requiresProtocol(inverse.subject, inverse.protocol))
567+
for (auto const& inverse : inverses) {
568+
// The Superclass and AnyObject checks here are based on the assumption that
569+
// a class cannot have an inverse applied to it. As a result, the existence
570+
// of a superclass bound always implies the existence of the conformance.
571+
// Thus, an inverse being present is a conflict.
572+
//
573+
// While AnyObject doesn't imply the conformance in the signature, we don't
574+
// want a generic parameter to be a class that can't be copied, since we
575+
// don't allow that for concrete classes today. Thus, we artificially
576+
// prevent AnyObject from being mixed with inverses.
577+
if (requiresProtocol(inverse.subject, inverse.protocol) ||
578+
getSuperclassBound(inverse.subject, getGenericParams()) ||
579+
requiresClass(inverse.subject))
569580
errors.push_back(
570581
RequirementError::forConflictingInverseRequirement(inverse,
571582
inverse.loc));
583+
}
572584
}
573585

574586
std::string RequirementMachine::getRuleAsStringForDiagnostics(

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,11 @@ void swift::rewriting::applyInverses(
820820
inverses[canSubject] = state;
821821
}
822822

823+
// Fast-path: if there are no valid inverses, then there are no requirements
824+
// to be removed.
825+
if (inverses.empty())
826+
return;
827+
823828
// Scan the structural requirements and cancel out any inferred requirements
824829
// based on the inverses we saw.
825830
result.erase(llvm::remove_if(result, [&](StructuralRequirement structReq) {

0 commit comments

Comments
 (0)