Skip to content

Commit db6a817

Browse files
committed
[GSB] Always ensure that we wire up typealiases in protocol extensions.
During "expansion" of the requirements of a protocol, we check all of the inherited associated types against definitions within the protocol. Also look for concrete types within extensions of that protocol, so we can identify more places where developers have used typealiases in inheriting protocols to effect a same-type constraint on an inherited associated type. Fixes SR-7097 / rdar://problem/38001269.
1 parent b2d7fb9 commit db6a817

File tree

2 files changed

+96
-41
lines changed

2 files changed

+96
-41
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3326,17 +3326,21 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement(
33263326
// Retrieve the requirement that a given typealias introduces when it
33273327
// overrides an inherited associated type with the same name, as a string
33283328
// suitable for use in a where clause.
3329-
auto getTypeAliasReq = [&](TypeAliasDecl *typealias, const char *start) {
3329+
auto getConcreteTypeReq = [&](TypeDecl *type, const char *start) {
33303330
std::string result;
33313331
{
33323332
llvm::raw_string_ostream out(result);
33333333
out << start;
3334-
out << typealias->getFullName() << " == ";
3335-
if (auto underlyingTypeRepr =
3336-
typealias->getUnderlyingTypeLoc().getTypeRepr())
3337-
underlyingTypeRepr->print(out);
3338-
else
3339-
typealias->getUnderlyingTypeLoc().getType().print(out);
3334+
out << type->getFullName() << " == ";
3335+
if (auto typealias = dyn_cast<TypeAliasDecl>(type)) {
3336+
if (auto underlyingTypeRepr =
3337+
typealias->getUnderlyingTypeLoc().getTypeRepr())
3338+
underlyingTypeRepr->print(out);
3339+
else
3340+
typealias->getUnderlyingTypeLoc().getType().print(out);
3341+
} else {
3342+
type->print(out);
3343+
}
33403344
}
33413345
return result;
33423346
};
@@ -3466,51 +3470,68 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement(
34663470
inheritedTypeDecls.erase(knownInherited);
34673471
continue;
34683472
}
3473+
}
34693474

3470-
if (auto typealias = dyn_cast<TypeAliasDecl>(Member)) {
3471-
// Check whether we inherited any types with the same name.
3472-
auto knownInherited = inheritedTypeDecls.find(typealias->getFullName());
3473-
if (knownInherited == inheritedTypeDecls.end()) continue;
3475+
// Check all remaining inherited type declarations to determine if
3476+
// this protocol has a non-associated-type type with the same name.
3477+
inheritedTypeDecls.remove_if(
3478+
[&](const std::pair<DeclName, TinyPtrVector<TypeDecl *>> &inherited) {
3479+
auto name = inherited.first;
3480+
for (auto found : proto->lookupDirect(name)) {
3481+
// We only want concrete type declarations.
3482+
auto type = dyn_cast<TypeDecl>(found);
3483+
if (!type || isa<AssociatedTypeDecl>(type)) continue;
34743484

3475-
bool shouldWarnAboutRedeclaration =
3476-
source->kind == RequirementSource::RequirementSignatureSelf;
3485+
// ... from the same module as the protocol.
3486+
if (type->getModuleContext() != proto->getModuleContext()) continue;
34773487

3478-
for (auto inheritedType : knownInherited->second) {
3479-
// If we have inherited associated type...
3480-
if (auto inheritedAssocTypeDecl =
3481-
dyn_cast<AssociatedTypeDecl>(inheritedType)) {
3482-
// Infer a same-type requirement between the typealias' underlying
3483-
// type and the inherited associated type.
3484-
addInferredSameTypeReq(inheritedAssocTypeDecl, typealias);
3488+
// Or is constrained.
3489+
if (auto ext = dyn_cast<ExtensionDecl>(type->getDeclContext())) {
3490+
if (ext->isConstrainedExtension()) continue;
3491+
}
34853492

3486-
// Warn that one should use where clauses for this.
3487-
if (shouldWarnAboutRedeclaration) {
3488-
auto inheritedFromProto = inheritedAssocTypeDecl->getProtocol();
3489-
auto fixItWhere = getProtocolWhereLoc();
3490-
Diags.diagnose(typealias,
3491-
diag::typealias_override_associated_type,
3492-
typealias->getFullName(),
3493-
inheritedFromProto->getDeclaredInterfaceType())
3494-
.fixItInsertAfter(fixItWhere.first,
3495-
getTypeAliasReq(typealias, fixItWhere.second))
3496-
.fixItRemove(typealias->getSourceRange());
3497-
Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here,
3498-
inheritedAssocTypeDecl->getFullName());
3493+
// We found something.
3494+
bool shouldWarnAboutRedeclaration =
3495+
source->kind == RequirementSource::RequirementSignatureSelf;
3496+
3497+
for (auto inheritedType : inherited.second) {
3498+
// If we have inherited associated type...
3499+
if (auto inheritedAssocTypeDecl =
3500+
dyn_cast<AssociatedTypeDecl>(inheritedType)) {
3501+
// Infer a same-type requirement between the typealias' underlying
3502+
// type and the inherited associated type.
3503+
addInferredSameTypeReq(inheritedAssocTypeDecl, type);
3504+
3505+
// Warn that one should use where clauses for this.
3506+
if (shouldWarnAboutRedeclaration) {
3507+
auto inheritedFromProto = inheritedAssocTypeDecl->getProtocol();
3508+
auto fixItWhere = getProtocolWhereLoc();
3509+
Diags.diagnose(type,
3510+
diag::typealias_override_associated_type,
3511+
name,
3512+
inheritedFromProto->getDeclaredInterfaceType())
3513+
.fixItInsertAfter(fixItWhere.first,
3514+
getConcreteTypeReq(type, fixItWhere.second))
3515+
.fixItRemove(type->getSourceRange());
3516+
Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here,
3517+
inheritedAssocTypeDecl->getFullName());
3518+
3519+
shouldWarnAboutRedeclaration = false;
3520+
}
34993521

3500-
shouldWarnAboutRedeclaration = false;
3522+
continue;
35013523
}
35023524

3503-
continue;
3525+
// Two typealiases that should be the same.
3526+
addInferredSameTypeReq(inheritedType, type);
35043527
}
35053528

3506-
// Two typealiases that should be the same.
3507-
addInferredSameTypeReq(inheritedType, typealias);
3529+
// We can remove this entry.
3530+
return true;
35083531
}
35093532

3510-
inheritedTypeDecls.erase(knownInherited);
3511-
continue;
3512-
}
3513-
}
3533+
return false;
3534+
});
35143535

35153536
// Infer same-type requirements among inherited type declarations.
35163537
for (auto &entry : inheritedTypeDecls) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s
2+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
3+
// RUN: %target-swift-frontend -primary-file %s -emit-ir -o -
4+
5+
protocol P1 { }
6+
7+
protocol P2 {
8+
associatedtype Assoc // expected-note{{'Assoc' declared here}}
9+
}
10+
11+
// CHECK-LABEL: .P3@
12+
// CHECK-NEXT: Requirement signature: <Self where Self : P2, Self.Assoc == ConformsToP1>
13+
protocol P3 : P2 { }
14+
15+
struct S0<M: P3> where M.Assoc: P1 { } // expected-warning{{redundant conformance constraint 'M.Assoc': 'P1'}}
16+
// expected-note@-1{{conformance constraint 'M.Assoc': 'P1' implied here}}
17+
18+
struct ConformsToP1: P1 { }
19+
20+
extension P3 {
21+
typealias Assoc = ConformsToP1 // expected-warning{{typealias overriding associated type 'Assoc' from protocol 'P2' is better expressed as same-type constraint on the protocol}}
22+
}
23+
24+
protocol P5 {
25+
}
26+
27+
extension P5 {
28+
// CHECK-LABEL: P5.testSR7097
29+
// CHECK-NEXT: Generic signature: <Self, M where Self : P5, M : P3>
30+
// CHECK-NEXT: <τ_0_0, τ_1_0 where τ_0_0 : P5, τ_1_0 : P3>
31+
func testSR7097<M>(_: S0<M>.Type) {}
32+
}
33+
34+

0 commit comments

Comments
 (0)