Skip to content

Commit 823d2a9

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 59edbdf commit 823d2a9

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
@@ -4011,17 +4011,21 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement(
40114011
// Retrieve the requirement that a given typealias introduces when it
40124012
// overrides an inherited associated type with the same name, as a string
40134013
// suitable for use in a where clause.
4014-
auto getTypeAliasReq = [&](TypeAliasDecl *typealias, const char *start) {
4014+
auto getConcreteTypeReq = [&](TypeDecl *type, const char *start) {
40154015
std::string result;
40164016
{
40174017
llvm::raw_string_ostream out(result);
40184018
out << start;
4019-
out << typealias->getFullName() << " == ";
4020-
if (auto underlyingTypeRepr =
4021-
typealias->getUnderlyingTypeLoc().getTypeRepr())
4022-
underlyingTypeRepr->print(out);
4023-
else
4024-
typealias->getUnderlyingTypeLoc().getType().print(out);
4019+
out << type->getFullName() << " == ";
4020+
if (auto typealias = dyn_cast<TypeAliasDecl>(type)) {
4021+
if (auto underlyingTypeRepr =
4022+
typealias->getUnderlyingTypeLoc().getTypeRepr())
4023+
underlyingTypeRepr->print(out);
4024+
else
4025+
typealias->getUnderlyingTypeLoc().getType().print(out);
4026+
} else {
4027+
type->print(out);
4028+
}
40254029
}
40264030
return result;
40274031
};
@@ -4151,51 +4155,68 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement(
41514155
inheritedTypeDecls.erase(knownInherited);
41524156
continue;
41534157
}
4158+
}
41544159

4155-
if (auto typealias = dyn_cast<TypeAliasDecl>(Member)) {
4156-
// Check whether we inherited any types with the same name.
4157-
auto knownInherited = inheritedTypeDecls.find(typealias->getFullName());
4158-
if (knownInherited == inheritedTypeDecls.end()) continue;
4160+
// Check all remaining inherited type declarations to determine if
4161+
// this protocol has a non-associated-type type with the same name.
4162+
inheritedTypeDecls.remove_if(
4163+
[&](const std::pair<DeclName, TinyPtrVector<TypeDecl *>> &inherited) {
4164+
auto name = inherited.first;
4165+
for (auto found : proto->lookupDirect(name)) {
4166+
// We only want concrete type declarations.
4167+
auto type = dyn_cast<TypeDecl>(found);
4168+
if (!type || isa<AssociatedTypeDecl>(type)) continue;
41594169

4160-
bool shouldWarnAboutRedeclaration =
4161-
source->kind == RequirementSource::RequirementSignatureSelf;
4170+
// ... from the same module as the protocol.
4171+
if (type->getModuleContext() != proto->getModuleContext()) continue;
41624172

4163-
for (auto inheritedType : knownInherited->second) {
4164-
// If we have inherited associated type...
4165-
if (auto inheritedAssocTypeDecl =
4166-
dyn_cast<AssociatedTypeDecl>(inheritedType)) {
4167-
// Infer a same-type requirement between the typealias' underlying
4168-
// type and the inherited associated type.
4169-
addInferredSameTypeReq(inheritedAssocTypeDecl, typealias);
4173+
// Or is constrained.
4174+
if (auto ext = dyn_cast<ExtensionDecl>(type->getDeclContext())) {
4175+
if (ext->isConstrainedExtension()) continue;
4176+
}
41704177

4171-
// Warn that one should use where clauses for this.
4172-
if (shouldWarnAboutRedeclaration) {
4173-
auto inheritedFromProto = inheritedAssocTypeDecl->getProtocol();
4174-
auto fixItWhere = getProtocolWhereLoc();
4175-
Diags.diagnose(typealias,
4176-
diag::typealias_override_associated_type,
4177-
typealias->getFullName(),
4178-
inheritedFromProto->getDeclaredInterfaceType())
4179-
.fixItInsertAfter(fixItWhere.first,
4180-
getTypeAliasReq(typealias, fixItWhere.second))
4181-
.fixItRemove(typealias->getSourceRange());
4182-
Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here,
4183-
inheritedAssocTypeDecl->getFullName());
4178+
// We found something.
4179+
bool shouldWarnAboutRedeclaration =
4180+
source->kind == RequirementSource::RequirementSignatureSelf;
4181+
4182+
for (auto inheritedType : inherited.second) {
4183+
// If we have inherited associated type...
4184+
if (auto inheritedAssocTypeDecl =
4185+
dyn_cast<AssociatedTypeDecl>(inheritedType)) {
4186+
// Infer a same-type requirement between the typealias' underlying
4187+
// type and the inherited associated type.
4188+
addInferredSameTypeReq(inheritedAssocTypeDecl, type);
4189+
4190+
// Warn that one should use where clauses for this.
4191+
if (shouldWarnAboutRedeclaration) {
4192+
auto inheritedFromProto = inheritedAssocTypeDecl->getProtocol();
4193+
auto fixItWhere = getProtocolWhereLoc();
4194+
Diags.diagnose(type,
4195+
diag::typealias_override_associated_type,
4196+
name,
4197+
inheritedFromProto->getDeclaredInterfaceType())
4198+
.fixItInsertAfter(fixItWhere.first,
4199+
getConcreteTypeReq(type, fixItWhere.second))
4200+
.fixItRemove(type->getSourceRange());
4201+
Diags.diagnose(inheritedAssocTypeDecl, diag::decl_declared_here,
4202+
inheritedAssocTypeDecl->getFullName());
4203+
4204+
shouldWarnAboutRedeclaration = false;
4205+
}
41844206

4185-
shouldWarnAboutRedeclaration = false;
4207+
continue;
41864208
}
41874209

4188-
continue;
4210+
// Two typealiases that should be the same.
4211+
addInferredSameTypeReq(inheritedType, type);
41894212
}
41904213

4191-
// Two typealiases that should be the same.
4192-
addInferredSameTypeReq(inheritedType, typealias);
4214+
// We can remove this entry.
4215+
return true;
41934216
}
41944217

4195-
inheritedTypeDecls.erase(knownInherited);
4196-
continue;
4197-
}
4198-
}
4218+
return false;
4219+
});
41994220

42004221
// Infer same-type requirements among inherited type declarations.
42014222
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)