Skip to content

Commit c7a60a8

Browse files
authored
Merge pull request #39174 from slavapestov/circular-generic-signature
Sema: Improved recovery from circular generic signature construction
2 parents ec9aa79 + 4a019f0 commit c7a60a8

File tree

12 files changed

+95
-54
lines changed

12 files changed

+95
-54
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2430,6 +2430,10 @@ ERROR(requires_generic_param_made_equal_to_concrete,none,
24302430
(Type))
24312431
ERROR(recursive_decl_reference,none,
24322432
"%0 %1 references itself", (DescriptiveDeclKind, DeclBaseName))
2433+
ERROR(recursive_generic_signature,none,
2434+
"%0 %1 has self-referential generic requirements", (DescriptiveDeclKind, DeclBaseName))
2435+
ERROR(recursive_generic_signature_extension,none,
2436+
"extension of %0 %1 has self-referential generic requirements", (DescriptiveDeclKind, DeclBaseName))
24332437
ERROR(recursive_same_type_constraint,none,
24342438
"same-type constraint %0 == %1 is recursive", (Type, Type))
24352439
ERROR(recursive_superclass_constraint,none,

include/swift/AST/TypeCheckRequests.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,8 @@ class GenericSignatureRequest :
14911491
bool isCached() const { return true; }
14921492
Optional<GenericSignature> getCachedResult() const;
14931493
void cacheResult(GenericSignature value) const;
1494+
1495+
void diagnoseCycle(DiagnosticEngine &diags) const;
14941496
};
14951497

14961498
/// Compute the underlying interface type of a typealias.

lib/AST/Decl.cpp

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -982,10 +982,47 @@ bool GenericContext::isComputingGenericSignature() const {
982982
GenericSignatureRequest{const_cast<GenericContext*>(this)});
983983
}
984984

985+
/// If we hit a cycle while building the generic signature, we can't return
986+
/// nullptr, since this breaks invariants elsewhere. Instead, build a dummy
987+
/// signature with no requirements.
988+
static GenericSignature getPlaceholderGenericSignature(
989+
const DeclContext *DC) {
990+
SmallVector<GenericParamList *, 2> gpLists;
991+
DC->forEachGenericContext([&](GenericParamList *genericParams) {
992+
gpLists.push_back(genericParams);
993+
});
994+
995+
if (gpLists.empty())
996+
return nullptr;
997+
998+
std::reverse(gpLists.begin(), gpLists.end());
999+
for (unsigned i : indices(gpLists))
1000+
gpLists[i]->setDepth(i);
1001+
1002+
SmallVector<GenericTypeParamType *, 2> result;
1003+
for (auto *genericParams : gpLists) {
1004+
for (auto *genericParam : *genericParams) {
1005+
result.push_back(genericParam->getDeclaredInterfaceType()
1006+
->castTo<GenericTypeParamType>());
1007+
}
1008+
}
1009+
1010+
return GenericSignature::get(result, {});
1011+
}
1012+
9851013
GenericSignature GenericContext::getGenericSignature() const {
986-
return evaluateOrDefault(
987-
getASTContext().evaluator,
988-
GenericSignatureRequest{const_cast<GenericContext *>(this)}, nullptr);
1014+
// Don't use evaluateOrDefault() here, because getting the 'default value'
1015+
// is slightly expensive here so we don't want to do it eagerly.
1016+
auto result = getASTContext().evaluator(
1017+
GenericSignatureRequest{const_cast<GenericContext *>(this)});
1018+
if (auto err = result.takeError()) {
1019+
llvm::handleAllErrors(std::move(err),
1020+
[](const CyclicalRequestError<GenericSignatureRequest> &E) {
1021+
// cycle detected
1022+
});
1023+
return getPlaceholderGenericSignature(this);
1024+
}
1025+
return *result;
9891026
}
9901027

9911028
GenericEnvironment *GenericContext::getGenericEnvironment() const {

lib/AST/DeclContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ void DeclContext::forEachGenericContext(
139139
if (auto *gpList = genericCtx->getGenericParams())
140140
fn(gpList);
141141
}
142-
} while ((dc = dc->getParent()));
142+
} while ((dc = dc->getParentForLookup()));
143143
}
144144

145145
unsigned DeclContext::getGenericContextDepth() const {

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3906,11 +3906,18 @@ bool GenericSignatureBuilder::addGenericParameterRequirements(
39063906
void GenericSignatureBuilder::addGenericParameter(GenericTypeParamType *GenericParam) {
39073907
auto params = getGenericParams();
39083908
(void)params;
3909-
assert(params.empty() ||
3910-
((GenericParam->getDepth() == params.back()->getDepth() &&
3911-
GenericParam->getIndex() == params.back()->getIndex() + 1) ||
3912-
(GenericParam->getDepth() > params.back()->getDepth() &&
3913-
GenericParam->getIndex() == 0)));
3909+
3910+
#ifndef NDEBUG
3911+
if (params.empty()) {
3912+
assert(GenericParam->getDepth() == 0);
3913+
assert(GenericParam->getIndex() == 0);
3914+
} else {
3915+
assert((GenericParam->getDepth() == params.back()->getDepth() &&
3916+
GenericParam->getIndex() == params.back()->getIndex() + 1) ||
3917+
(GenericParam->getDepth() > params.back()->getDepth() &&
3918+
GenericParam->getIndex() == 0));
3919+
}
3920+
#endif
39143921

39153922
// Create a potential archetype for this type parameter.
39163923
auto PA = new (Impl->Allocator) PotentialArchetype(GenericParam);

lib/AST/TypeCheckRequests.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,22 @@ void GenericSignatureRequest::cacheResult(GenericSignature value) const {
738738
GC->GenericSigAndBit.setPointerAndInt(value, true);
739739
}
740740

741+
void GenericSignatureRequest::diagnoseCycle(DiagnosticEngine &diags) const {
742+
auto *GC = std::get<0>(getStorage());
743+
auto *D = GC->getAsDecl();
744+
745+
if (auto *VD = dyn_cast<ValueDecl>(D)) {
746+
VD->diagnose(diag::recursive_generic_signature,
747+
VD->getDescriptiveKind(), VD->getBaseName());
748+
} else {
749+
auto *ED = cast<ExtensionDecl>(D);
750+
auto *NTD = ED->getExtendedNominal();
751+
752+
ED->diagnose(diag::recursive_generic_signature_extension,
753+
NTD->getDescriptiveKind(), NTD->getName());
754+
}
755+
}
756+
741757
//----------------------------------------------------------------------------//
742758
// InferredGenericSignatureRequest computation.
743759
//----------------------------------------------------------------------------//

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ static Type formExtensionInterfaceType(
597597
if (!nominal->isGeneric() || isa<ProtocolDecl>(nominal)) {
598598
resultType = NominalType::get(nominal, parentType,
599599
nominal->getASTContext());
600-
} else {
600+
} else if (genericParams) {
601601
auto currentBoundType = type->getAs<BoundGenericType>();
602602

603603
// Form the bound generic type with the type parameters provided.

lib/Sema/TypeCheckType.cpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,6 @@ bool TypeChecker::checkContextualRequirements(GenericTypeDecl *decl,
590590
return true;
591591
}
592592

593-
auto &ctx = dc->getASTContext();
594-
595593
SourceLoc noteLoc;
596594
{
597595
// We are interested in either a contextual where clause or
@@ -610,14 +608,6 @@ bool TypeChecker::checkContextualRequirements(GenericTypeDecl *decl,
610608

611609
const auto subMap = parentTy->getContextSubstitutions(decl->getDeclContext());
612610
const auto genericSig = decl->getGenericSignature();
613-
if (!genericSig) {
614-
if (loc.isValid()) {
615-
ctx.Diags.diagnose(loc, diag::recursive_decl_reference,
616-
decl->getDescriptiveKind(), decl->getName());
617-
decl->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
618-
}
619-
return false;
620-
}
621611

622612
const auto result =
623613
TypeChecker::checkGenericArguments(
@@ -765,14 +755,6 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
765755
}
766756
}
767757

768-
// FIXME: More principled handling of circularity.
769-
if (!decl->getGenericSignature()) {
770-
diags.diagnose(loc, diag::recursive_decl_reference,
771-
decl->getDescriptiveKind(), decl->getName());
772-
decl->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
773-
return ErrorType::get(ctx);
774-
}
775-
776758
// Resolve the types of the generic arguments.
777759
auto genericResolution =
778760
resolution.withOptions(adjustOptionsForGenericArgs(options));

test/Generics/generic_types.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,9 @@ var y: X5<X4, Int> // expected-error{{'X5' requires the types 'X4.AssocP' (aka '
228228
// Recursive generic signature validation.
229229
class Top {}
230230
class Bottom<T : Bottom<Top>> {}
231-
// expected-error@-1 {{generic class 'Bottom' references itself}}
232-
// expected-note@-2 {{type declared here}}
233-
// expected-error@-3 {{circular reference}}
231+
// expected-error@-1 {{'Bottom' requires that 'Top' inherit from 'Bottom<Top>'}}
232+
// expected-note@-2 {{requirement specified as 'T' : 'Bottom<Top>' [with T = Top]}}
233+
// expected-error@-3 {{generic class 'Bottom' has self-referential generic requirements}}
234234
// expected-note@-4 {{while resolving type 'Bottom<Top>'}}
235235
// expected-note@-5 {{through reference here}}
236236

test/decl/protocol/req/recursion.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,16 @@ public protocol P {
4343
associatedtype T
4444
}
4545

46-
public struct S<A: P> where A.T == S<A> { // expected-error {{circular reference}}
47-
// expected-note@-1 {{type declared here}}
48-
// expected-error@-2 {{generic struct 'S' references itself}}
49-
// expected-note@-3 {{while resolving type 'S<A>'}}
46+
public struct S<A: P> where A.T == S<A> {
47+
// expected-error@-1 {{generic struct 'S' has self-referential generic requirements}}
48+
// expected-note@-2 {{while resolving type 'S<A>'}}
5049
func f(a: A.T) {
5150
g(a: id(t: a)) // `a` has error type which is diagnosed as circular reference
52-
// expected-error@-1 {{conflicting arguments to generic parameter 'T' ('A.T' (associated type of protocol 'P') vs. 'S<A>')}}
5351
_ = A.T.self
5452
}
5553

5654
func g(a: S<A>) {
5755
f(a: id(t: a))
58-
// expected-error@-1 {{conflicting arguments to generic parameter 'T' ('S<A>' vs. 'A.T' (associated type of protocol 'P'))}}
5956
_ = S<A>.self
6057
}
6158

@@ -72,10 +69,9 @@ protocol PI {
7269
associatedtype T : I
7370
}
7471

75-
struct SI<A: PI> : I where A : I, A.T == SI<A> { // expected-error {{circular reference}}
76-
// expected-note@-1 {{type declared here}}
77-
// expected-error@-2 {{generic struct 'SI' references itself}}
78-
// expected-note@-3 {{while resolving type 'SI<A>'}}
72+
struct SI<A: PI> : I where A : I, A.T == SI<A> {
73+
// expected-error@-1 {{generic struct 'SI' has self-referential generic requirements}}
74+
// expected-note@-2 {{while resolving type 'SI<A>'}}
7975
func ggg<T : I>(t: T.Type) -> T {
8076
return T()
8177
}

validation-test/compiler_crashers_2_fixed/0161-sr6569.swift

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,10 @@ protocol P {
66

77
struct Type<Param> {}
88
extension Type: P where Param: P, Param.A == Type<Param> {
9-
// expected-error@-1 {{circular reference}}
10-
// expected-note@-2 {{through reference here}}
11-
// expected-error@-3 {{circular reference}}
12-
// expected-note@-4 {{through reference here}}
13-
// expected-error@-5 {{circular reference}}
14-
// expected-note@-6 {{through reference here}}
15-
// expected-error@-7 {{type 'Type<Param>' does not conform to protocol 'P'}}
9+
// expected-error@-1 5{{extension of generic struct 'Type' has self-referential generic requirements}}
10+
// expected-note@-2 5{{through reference here}}
11+
// expected-error@-3 {{type 'Type<Param>' does not conform to protocol 'P'}}
1612
typealias A = Param
17-
// expected-note@-1 {{through reference here}}
18-
// expected-note@-2 {{through reference here}}
13+
// expected-note@-1 2{{through reference here}}
14+
// expected-note@-2 {{possibly intended match 'Type<Param>.A' (aka 'Param') does not conform to 'P'}}
1915
}

validation-test/compiler_crashers_2_fixed/0163-sr8033.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ struct Foo<T> {}
55
protocol P1 {
66
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
77
}
8-
extension Foo: P1 where A : P1 {} // expected-error {{circular reference}}
9-
// expected-note@-1 {{while resolving type 'A'}}
10-
// expected-note@-2 {{through reference here}}
11-
// expected-error@-3 {{type 'Foo<T>' does not conform to protocol 'P1'}}
8+
extension Foo: P1 where A : P1 {}
9+
// expected-error@-1 {{extension of generic struct 'Foo' has self-referential generic requirements}}
10+
// expected-note@-2 {{while resolving type 'A'}}
11+
// expected-note@-3 {{through reference here}}
12+
// expected-error@-4 {{type 'Foo<T>' does not conform to protocol 'P1'}}

0 commit comments

Comments
 (0)