Sema: Teach TypeReprCycleCheckWalker to avoid more cycles

More declaration order issues uncovered by lazy type checking.

Fixes rdar://119499800.
This commit is contained in:
Slava Pestov
2023-12-11 12:57:32 -05:00
parent a8675de47c
commit a576984e2f
2 changed files with 70 additions and 9 deletions

View File

@@ -165,27 +165,25 @@ namespace {
/// Try to avoid situations where resolving the type of a witness calls back
/// into associated type inference.
struct TypeReprCycleCheckWalker : ASTWalker {
ASTContext &ctx;
llvm::SmallDenseSet<Identifier, 2> circularNames;
ValueDecl *witness;
bool found;
TypeReprCycleCheckWalker(
ASTContext &ctx,
const llvm::SetVector<AssociatedTypeDecl *> &allUnresolved)
: witness(nullptr), found(false) {
: ctx(ctx), witness(nullptr), found(false) {
for (auto *assocType : allUnresolved) {
circularNames.insert(assocType->getName());
}
}
PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
// FIXME: We should still visit any generic arguments of this member type.
// However, we want to skip 'Foo.Element' because the 'Element' reference is
// not unqualified.
if (auto *memberTyR = dyn_cast<MemberTypeRepr>(T)) {
return Action::SkipChildren();
}
// FIXME: Visit generic arguments.
if (auto *identTyR = dyn_cast<SimpleIdentTypeRepr>(T)) {
// If we're inferring `Foo`, don't look at a witness mentioning `Foo`.
if (circularNames.count(identTyR->getNameRef().getBaseIdentifier()) > 0) {
// If unqualified lookup can find a type with this name without looking
// into protocol members, don't skip the witness, since this type might
@@ -194,7 +192,6 @@ struct TypeReprCycleCheckWalker : ASTWalker {
identTyR->getNameRef(), witness->getDeclContext(),
identTyR->getLoc(), UnqualifiedLookupOptions());
auto &ctx = witness->getASTContext();
auto results =
evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {});
@@ -207,6 +204,34 @@ struct TypeReprCycleCheckWalker : ASTWalker {
}
}
if (auto *memberTyR = dyn_cast<MemberTypeRepr>(T)) {
// If we're looking at a member type`Foo.Bar`, check `Foo` recursively.
auto *baseTyR = memberTyR->getBaseComponent();
baseTyR->walk(*this);
// If we're inferring `Foo`, don't look at a witness mentioning `Self.Foo`.
if (auto *identTyR = dyn_cast<SimpleIdentTypeRepr>(baseTyR)) {
if (identTyR->getNameRef().getBaseIdentifier() == ctx.Id_Self) {
// But if qualified lookup can find a type with this name without
// looking into protocol members, don't skip the witness, since this
// type might be a candidate witness.
SmallVector<ValueDecl *, 2> results;
witness->getInnermostDeclContext()->lookupQualified(
witness->getDeclContext()->getSelfTypeInContext(),
identTyR->getNameRef(), SourceLoc(), NLOptions(), results);
// Ok, resolving this member type would trigger associated type
// inference recursively. We're going to skip this witness.
if (results.empty()) {
found = true;
return Action::Stop();
}
}
}
return Action::SkipChildren();
}
return Action::Continue();
}
@@ -296,7 +321,7 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
abort();
}
TypeReprCycleCheckWalker cycleCheck(allUnresolved);
TypeReprCycleCheckWalker cycleCheck(dc->getASTContext(), allUnresolved);
InferredAssociatedTypesByWitnesses result;

View File

@@ -95,3 +95,39 @@ public enum CaseWitness: CaseProtocol {
case b(_: A)
case c(_: A)
}
// rdar://119499800 #1
public typealias A8 = Batch.Iterator
public struct Batch: Collection {
public typealias Element = Int
public typealias Index = Array<Element>.Index
var elements: [Element]
init(_ elements: some Collection<Element>) {
self.elements = Array(elements)
}
public var startIndex: Index { return elements.startIndex }
public var endIndex: Index { return elements.endIndex }
public subscript(index: Index) -> Iterator.Element {
return elements[index]
}
public func index(after i: Index) -> Index {
return elements.index(after: i)
}
}
// rdar://119499800 #2
public typealias A9 = LogTypes.RawValue
public struct LogTypes: OptionSet {
public init(rawValue: Self.RawValue) {
self.rawValue = rawValue
}
public let rawValue: Int
}