Skip to content

Commit 244d2af

Browse files
committed
RequirementMachine: New way of propagating failure when building rewrite system for protocol
If we failed to construct a rewrite system for a protocol, either because the Knuth-Bendix algorithm failed or because of a request cycle while resolving requirements, we would end up in a situation where the resulting rewrite system didn't include all conformance requirements and associated types, so name lookup would find declarations whose interface types are not valid type parameters. Fix this by propagating failure better and just doing nothing in getReducedTypeParameter(). Fixes rdar://147277543.
1 parent 21214be commit 244d2af

File tree

15 files changed

+67
-47
lines changed

15 files changed

+67
-47
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,11 @@ enum class GenericSignatureErrorFlags {
606606

607607
/// The Knuth-Bendix completion procedure failed to construct a confluent
608608
/// rewrite system.
609-
CompletionFailed = (1<<2)
609+
CompletionFailed = (1<<2),
610+
611+
/// A request evaluator cycle prevented us from computing this generic
612+
/// signature.
613+
CircularReference = (1<<3),
610614
};
611615

612616
using GenericSignatureErrors = OptionSet<GenericSignatureErrorFlags>;

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7451,7 +7451,7 @@ RequirementSignature ProtocolDecl::getRequirementSignature() const {
74517451
RequirementSignatureRequest{const_cast<ProtocolDecl *>(this)},
74527452
[this]() {
74537453
return RequirementSignature::getPlaceholderRequirementSignature(
7454-
this, GenericSignatureErrors());
7454+
this, GenericSignatureErrorFlags::CircularReference);
74557455
});
74567456
}
74577457

lib/AST/GenericSignature.cpp

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,33 +1400,13 @@ RequirementSignature RequirementSignature::getPlaceholderRequirementSignature(
14001400
const ProtocolDecl *proto, GenericSignatureErrors errors) {
14011401
auto &ctx = proto->getASTContext();
14021402

1403-
SmallVector<ProtocolDecl *, 2> inheritedProtos;
1404-
for (auto *inheritedProto : proto->getInheritedProtocols()) {
1405-
inheritedProtos.push_back(inheritedProto);
1406-
}
1407-
1403+
// Pretend the protocol inherits from Copyable and Escapable.
1404+
SmallVector<Requirement, 2> requirements;
14081405
for (auto ip : InvertibleProtocolSet::allKnown()) {
14091406
auto *otherProto = ctx.getProtocol(getKnownProtocolKind(ip));
1410-
inheritedProtos.push_back(otherProto);
1411-
}
1412-
1413-
ProtocolType::canonicalizeProtocols(inheritedProtos);
1414-
1415-
SmallVector<Requirement, 2> requirements;
1416-
1417-
for (auto *inheritedProto : inheritedProtos) {
14181407
requirements.emplace_back(RequirementKind::Conformance,
14191408
proto->getSelfInterfaceType(),
1420-
inheritedProto->getDeclaredInterfaceType());
1421-
}
1422-
1423-
for (auto *assocTypeDecl : proto->getAssociatedTypeMembers()) {
1424-
for (auto ip : InvertibleProtocolSet::allKnown()) {
1425-
auto *otherProto = ctx.getProtocol(getKnownProtocolKind(ip));
1426-
requirements.emplace_back(RequirementKind::Conformance,
1427-
assocTypeDecl->getDeclaredInterfaceType(),
1428-
otherProto->getDeclaredInterfaceType());
1429-
}
1409+
otherProto->getDeclaredInterfaceType());
14301410
}
14311411

14321412
// Maintain invariants.

lib/AST/RequirementMachine/GenericSignatureQueries.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ static Type substPrefixType(Type type, unsigned suffixLength, Type prefixType,
353353
Type RequirementMachine::getReducedTypeParameter(
354354
CanType t,
355355
ArrayRef<GenericTypeParamType *> genericParams) const {
356+
if (Failed)
357+
return ErrorType::get(t);
358+
356359
// Get a simplified term T.
357360
auto term = Context.getMutableTermForType(t, /*proto=*/nullptr);
358361
System.simplify(term);
@@ -801,6 +804,9 @@ void RequirementMachine::verify(const MutableTerm &term) const {
801804
}
802805
}
803806

807+
if (Failed)
808+
return;
809+
804810
MutableTerm erased;
805811

806812
// First, "erase" resolved associated types from the term, and try

lib/AST/RequirementMachine/RequirementBuilder.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,9 @@ RequirementMachine::buildRequirementsFromRules(
395395
bool reconstituteSugar,
396396
std::vector<Requirement> &reqs,
397397
std::vector<ProtocolTypeAlias> &aliases) const {
398+
if (Failed)
399+
return;
400+
398401
RequirementBuilder builder(System, Map, genericParams, reconstituteSugar);
399402

400403
builder.addRequirementRules(requirementRules);

lib/AST/RequirementMachine/RequirementMachine.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,9 @@ RequirementMachine::initWithProtocolSignatureRequirements(
289289
RuleBuilder builder(Context, System.getReferencedProtocols());
290290
builder.initWithProtocolSignatureRequirements(protos);
291291

292+
// Remember if any of our upstream protocols failed to complete.
293+
Failed = builder.Failed;
294+
292295
// Add the initial set of rewrite rules to the rewrite system.
293296
System.initialize(/*recordLoops=*/false, protos,
294297
std::move(builder.ImportedRules),
@@ -337,6 +340,9 @@ RequirementMachine::initWithGenericSignature(GenericSignature sig) {
337340
builder.initWithGenericSignature(sig.getGenericParams(),
338341
sig.getRequirements());
339342

343+
// Remember if any of our upstream protocols failed to complete.
344+
Failed = builder.Failed;
345+
340346
// Add the initial set of rewrite rules to the rewrite system.
341347
System.initialize(/*recordLoops=*/false,
342348
/*protos=*/ArrayRef<const ProtocolDecl *>(),
@@ -390,6 +396,9 @@ RequirementMachine::initWithProtocolWrittenRequirements(
390396
RuleBuilder builder(Context, System.getReferencedProtocols());
391397
builder.initWithProtocolWrittenRequirements(component, protos);
392398

399+
// Remember if any of our upstream protocols failed to complete.
400+
Failed = builder.Failed;
401+
393402
// Add the initial set of rewrite rules to the rewrite system.
394403
System.initialize(/*recordLoops=*/true, component,
395404
std::move(builder.ImportedRules),
@@ -437,6 +446,9 @@ RequirementMachine::initWithWrittenRequirements(
437446
RuleBuilder builder(Context, System.getReferencedProtocols());
438447
builder.initWithWrittenRequirements(genericParams, requirements);
439448

449+
// Remember if any of our upstream protocols failed to complete.
450+
Failed = builder.Failed;
451+
440452
// Add the initial set of rewrite rules to the rewrite system.
441453
System.initialize(/*recordLoops=*/true,
442454
/*protos=*/ArrayRef<const ProtocolDecl *>(),
@@ -553,10 +565,6 @@ ArrayRef<Rule> RequirementMachine::getLocalRules() const {
553565
return System.getLocalRules();
554566
}
555567

556-
bool RequirementMachine::isComplete() const {
557-
return Complete;
558-
}
559-
560568
GenericSignatureErrors RequirementMachine::getErrors() const {
561569
// FIXME: Assert if we had errors but we didn't emit any diagnostics?
562570
return System.getErrors();

lib/AST/RequirementMachine/RequirementMachine.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ class RequirementMachine final {
6464
bool Dump = false;
6565
bool Complete = false;
6666

67+
/// Whether completion failed, either for this rewrite system, or one of
68+
/// its imported protocols. In this case, name lookup might find type
69+
/// parameters that are not valid according to the rewrite system, because
70+
/// not all conformance requirements will be present. This flag allows
71+
/// us to skip certain verification checks in that case.
72+
bool Failed = false;
73+
6774
/// Parameters to prevent runaway completion and property map construction.
6875
unsigned MaxRuleCount;
6976
unsigned MaxRuleLength;
@@ -110,7 +117,9 @@ class RequirementMachine final {
110117
ArrayRef<GenericTypeParamType *> genericParams,
111118
ArrayRef<StructuralRequirement> requirements);
112119

113-
bool isComplete() const;
120+
bool isComplete() const {
121+
return Complete;
122+
}
114123

115124
std::pair<CompletionResult, unsigned>
116125
computeCompletion(RewriteSystem::ValidityPolicy policy);
@@ -139,6 +148,10 @@ class RequirementMachine final {
139148
public:
140149
~RequirementMachine();
141150

151+
bool isFailed() const {
152+
return Failed;
153+
}
154+
142155
// Generic signature queries. Generally you shouldn't have to construct a
143156
// RequirementMachine instance; instead, call the corresponding methods on
144157
// GenericSignature, which lazily create a RequirementMachine for you.

lib/AST/RequirementMachine/RuleBuilder.cpp

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -106,19 +106,10 @@ void RuleBuilder::initWithProtocolSignatureRequirements(
106106
addPermanentProtocolRules(proto);
107107

108108
auto reqs = proto->getRequirementSignature();
109-
110-
// If completion failed, we'll have a totally empty requirement signature,
111-
// but to maintain invariants around what constitutes a valid rewrite term
112-
// between getTypeForTerm() and isValidTypeParameter(), we need to add rules
113-
// for inherited protocols.
114-
if (reqs.getErrors().contains(GenericSignatureErrorFlags::CompletionFailed)) {
115-
for (auto *inheritedProto : proto->getAllInheritedProtocols()) {
116-
Requirement req(RequirementKind::Conformance,
117-
proto->getSelfInterfaceType(),
118-
inheritedProto->getDeclaredInterfaceType());
119-
120-
addRequirement(req.getCanonical(), proto);
121-
}
109+
auto errors = reqs.getErrors();
110+
if (errors.contains(GenericSignatureErrorFlags::CompletionFailed) ||
111+
errors.contains(GenericSignatureErrorFlags::CircularReference)) {
112+
Failed = 1;
122113
}
123114

124115
for (auto req : reqs.getRequirements())
@@ -495,6 +486,9 @@ void RuleBuilder::collectRulesFromReferencedProtocols() {
495486
continue;
496487
}
497488

489+
if (machine->isFailed())
490+
Failed = 1;
491+
498492
// We grab the machine's local rules, not *all* of its rules, to avoid
499493
// duplicates in case multiple machines share a dependency on a downstream
500494
// protocol component.

lib/AST/RequirementMachine/RuleBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,15 @@ struct RuleBuilder {
8383
/// Used to ensure the initWith*() methods are only called once.
8484
unsigned Initialized : 1;
8585

86+
/// Whether completion failed for any of our upstream protocol components.
87+
unsigned Failed : 1;
88+
8689
RuleBuilder(RewriteContext &ctx,
8790
llvm::DenseSet<const ProtocolDecl *> &referencedProtocols)
8891
: Context(ctx), ReferencedProtocols(referencedProtocols) {
8992
Dump = ctx.getASTContext().LangOpts.DumpRequirementMachine;
9093
Initialized = 0;
94+
Failed = 0;
9195
}
9296

9397
void initWithGenericSignature(ArrayRef<GenericTypeParamType *> genericParams,

test/Generics/non_fcrs.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ protocol P2 {
3131
associatedtype T : P2
3232
}
3333

34+
protocol P12: P1, P2 {}
35+
// expected-error@-1 {{cannot build rewrite system for protocol; rule length limit exceeded}}
36+
// expected-note@-2 {{failed rewrite rule is [P12:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P2] => [P12:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T]}}
37+
3438
func foo<T : P1 & P2>(_: T) {}
3539
// expected-error@-1 {{cannot build rewrite system for generic signature; rule length limit exceeded}}
3640
// expected-note@-2 {{failed rewrite rule is τ_0_0.[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P2] => τ_0_0.[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T].[P1:T]}}

0 commit comments

Comments
 (0)