Skip to content

Commit 161f309

Browse files
committed
Improve recursion-breaking when resolving type witnesses.
Specifically track when we're resolving type witnesses for a given conformance, and refuse to recursively attempt to resolve those type witnesses. The standard library patch in rdar://problem/21789238 triggers this, and isolating it in a small test case has proven illusive. Swift SVN r30160
1 parent 0e35bad commit 161f309

File tree

7 files changed

+131
-81
lines changed

7 files changed

+131
-81
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,16 @@ enum class ProtocolConformanceKind {
6969
};
7070

7171
/// Describes the state of a protocol conformance, which may be complete,
72-
/// incomplete, or invalid.
72+
/// incomplete, or currently being checked.
7373
enum class ProtocolConformanceState {
74-
/// The conformance has been fully checked and is complete and well-formed.
74+
/// The conformance has been fully checked.
7575
Complete,
7676
/// The conformance is known but is not yet complete.
7777
Incomplete,
78+
/// The conformance's type witnesses are currently being resolved.
79+
CheckingTypeWitnesses,
7880
/// The conformance is being checked.
7981
Checking,
80-
/// The conformance has been found to be invalid and should not be
81-
/// used.
82-
Invalid
8382
};
8483

8584
/// \brief Describes how a particular type conforms to a given protocol,
@@ -119,19 +118,18 @@ class ProtocolConformance {
119118
/// Retrieve the state of this conformance.
120119
ProtocolConformanceState getState() const;
121120

122-
/// Determine whether this conformance is complete and well-formed.
121+
/// Determine whether this conformance is complete.
123122
bool isComplete() const {
124123
return getState() == ProtocolConformanceState::Complete;
125124
}
126125

127126
/// Determine whether this conformance is invalid.
128-
bool isInvalid() const {
129-
return getState() == ProtocolConformanceState::Invalid;
130-
}
127+
bool isInvalid() const;
131128

132129
/// Determine whether this conformance is incomplete.
133130
bool isIncomplete() const {
134131
return getState() == ProtocolConformanceState::Incomplete ||
132+
getState() == ProtocolConformanceState::CheckingTypeWitnesses ||
135133
getState() == ProtocolConformanceState::Checking;
136134
}
137135

@@ -295,7 +293,9 @@ class NormalProtocolConformance : public ProtocolConformance,
295293

296294
/// The declaration context containing the ExtensionDecl or
297295
/// NominalTypeDecl that declared the conformance.
298-
DeclContext *DC;
296+
///
297+
/// Also stores the "invalid" bit.
298+
llvm::PointerIntPair<DeclContext *, 1, bool> DCAndInvalid;
299299

300300
/// \brief The mapping of individual requirements in the protocol over to
301301
/// the declarations that satisfy those requirements.
@@ -319,7 +319,7 @@ class NormalProtocolConformance : public ProtocolConformance,
319319
SourceLoc loc, DeclContext *dc,
320320
ProtocolConformanceState state)
321321
: ProtocolConformance(ProtocolConformanceKind::Normal, conformingType),
322-
ProtocolAndState(protocol, state), Loc(loc), DC(dc)
322+
ProtocolAndState(protocol, state), Loc(loc), DCAndInvalid(dc, false)
323323
{
324324
}
325325

@@ -332,7 +332,7 @@ class NormalProtocolConformance : public ProtocolConformance,
332332

333333
/// Get the declaration context that contains the conforming extension or
334334
/// nominal type declaration.
335-
DeclContext *getDeclContext() const { return DC; }
335+
DeclContext *getDeclContext() const { return DCAndInvalid.getPointer(); }
336336

337337
/// Retrieve the state of this conformance.
338338
ProtocolConformanceState getState() const {
@@ -344,6 +344,12 @@ class NormalProtocolConformance : public ProtocolConformance,
344344
ProtocolAndState.setInt(state);
345345
}
346346

347+
/// Determine whether this conformance is invalid.
348+
bool isInvalid() const { return DCAndInvalid.getInt(); }
349+
350+
/// Mark this conformance as invalid.
351+
void setInvalid() { DCAndInvalid.setInt(true); }
352+
347353
/// Retrieve the type witness substitution and type decl (if one exists)
348354
/// for the given associated type.
349355
std::pair<const Substitution &, TypeDecl *>
@@ -628,6 +634,10 @@ class InheritedProtocolConformance : public ProtocolConformance,
628634
}
629635
};
630636

637+
inline bool ProtocolConformance::isInvalid() const {
638+
return getRootNormalConformance()->isInvalid();
639+
}
640+
631641
} // end namespace swift
632642

633643
#endif // LLVM_SWIFT_AST_PROTOCOLCONFORMANCE_H

lib/AST/Module.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,11 @@ ArrayRef<Substitution> BoundGenericType::getSubstitutions(
731731
++index;
732732
}
733733

734+
// Before recording substitutions, make sure we didn't end up doing it
735+
// recursively.
736+
if (auto known = ctx.getSubstitutions(canon, gpContext))
737+
return *known;
738+
734739
// Copy and record the substitutions.
735740
auto permanentSubs = ctx.AllocateCopy(resultSubstitutions,
736741
hasTypeVariables

lib/AST/ProtocolConformance.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ ConcreteDeclRef NormalProtocolConformance::getWitness(
233233
if (known != Mapping.end()) {
234234
return known->second;
235235
} else {
236-
assert(!isComplete() && "Resolver did not resolve requirement");
236+
assert((!isComplete() || isInvalid()) &&
237+
"Resolver did not resolve requirement");
237238
return ConcreteDeclRef();
238239
}
239240
}

lib/AST/Type.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,11 +2497,13 @@ static Type getMemberForBaseType(Module *module,
24972497
return Type();
24982498

24992499
case ConformanceKind::Conforms:
2500-
// If we're supposed to skip this conformance's unsatisfied type
2500+
// If we have an unsatisfied type witness while we're checking the
2501+
// conformances we're supposed to skip this conformance's unsatisfied type
25012502
// witnesses, and we have an unsatisfied type witness, return
25022503
// "missing".
2503-
if (conformance.getPointer()->getRootNormalConformance()
2504-
== options.getSkippedConformance() &&
2504+
if (conformance.getPointer() &&
2505+
conformance.getPointer()->getRootNormalConformance()->getState()
2506+
== ProtocolConformanceState::CheckingTypeWitnesses &&
25052507
!conformance.getPointer()->hasTypeWitness(assocType, nullptr))
25062508
return Type();
25072509

lib/AST/Verifier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1661,14 +1661,14 @@ struct ASTNodeBase {};
16611661

16621662
switch (conformance->getState()) {
16631663
case ProtocolConformanceState::Complete:
1664-
case ProtocolConformanceState::Invalid:
16651664
// More checking below.
16661665
break;
16671666

16681667
case ProtocolConformanceState::Incomplete:
16691668
// Ignore incomplete conformances; we didn't need them.
16701669
return;
16711670

1671+
case ProtocolConformanceState::CheckingTypeWitnesses:
16721672
case ProtocolConformanceState::Checking:
16731673
dumpRef(decl);
16741674
Out << " has a protocol conformance that is still being checked "

0 commit comments

Comments
 (0)