Skip to content

Commit 87e3d02

Browse files
authored
Merge pull request #4224 from slavapestov/protocol-typealias-member-access-fix
Sema: Allow protocol typealiases to be accessed from expression conte…
2 parents ca7beda + a485e52 commit 87e3d02

File tree

7 files changed

+74
-28
lines changed

7 files changed

+74
-28
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,10 +1434,11 @@ NOTE(optional_req_near_match_accessibility,none,
14341434

14351435
// Protocols and existentials
14361436
ERROR(assoc_type_outside_of_protocol,none,
1437-
"cannot use associated type %0 outside of its protocol", (Identifier))
1438-
ERROR(typealias_to_assoc_type_outside_of_protocol,none,
1439-
"cannot use typealias %0 of associated type %1 outside of its protocol",
1440-
(Identifier, TypeLoc))
1437+
"associated type %0 can only be used with a concrete type or "
1438+
"generic parameter base", (Identifier))
1439+
ERROR(typealias_outside_of_protocol,none,
1440+
"typealias %0 can only be used with a concrete type or "
1441+
"generic parameter base", (Identifier))
14411442

14421443
ERROR(circular_protocol_def,none,
14431444
"circular protocol inheritance %0", (StringRef))

lib/Sema/CSDiag.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2349,14 +2349,32 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy,
23492349
SourceLoc loc) {
23502350
SourceRange baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange();
23512351

2352+
Optional<InFlightDiagnostic> Diag;
2353+
23522354
// If the base of the lookup is a protocol metatype, suggest
23532355
// to replace the metatype with 'Self'
23542356
// error saying the lookup cannot be on a protocol metatype
23552357
if (auto metatypeTy = baseObjTy->getAs<MetatypeType>()) {
2356-
auto Diag = diagnose(loc,
2357-
diag::could_not_use_type_member_on_protocol_metatype,
2358-
baseObjTy, memberName);
2359-
Diag.highlight(baseRange).highlight(nameLoc.getSourceRange());
2358+
assert(metatypeTy->getInstanceType()->isExistentialType());
2359+
2360+
// Give a customized message if we're accessing a member type
2361+
// of a protocol -- otherwise a diagnostic talking about
2362+
// static members doesn't make a whole lot of sense
2363+
if (isa<TypeAliasDecl>(member)) {
2364+
Diag.emplace(diagnose(loc,
2365+
diag::typealias_outside_of_protocol,
2366+
memberName.getBaseName()));
2367+
} else if (isa<AssociatedTypeDecl>(member)) {
2368+
Diag.emplace(diagnose(loc,
2369+
diag::assoc_type_outside_of_protocol,
2370+
memberName.getBaseName()));
2371+
} else {
2372+
Diag.emplace(diagnose(loc,
2373+
diag::could_not_use_type_member_on_protocol_metatype,
2374+
baseObjTy, memberName));
2375+
}
2376+
2377+
Diag->highlight(baseRange).highlight(nameLoc.getSourceRange());
23602378

23612379
// See through function decl context
23622380
if (auto parent = CS->DC->getInnermostTypeContext()) {
@@ -2365,18 +2383,14 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy,
23652383
if (auto extensionContext = parent->getAsProtocolExtensionContext()) {
23662384
if (extensionContext->getDeclaredType()->getCanonicalType()
23672385
== metatypeTy->getInstanceType()->getCanonicalType()) {
2368-
Diag.fixItReplace(baseRange, "Self");
2386+
Diag->fixItReplace(baseRange, "Self");
23692387
}
23702388
}
23712389
}
23722390

23732391
return;
23742392
}
23752393

2376-
// Otherwise the static member lookup was invalid because it was
2377-
// called on an instance
2378-
Optional<InFlightDiagnostic> Diag;
2379-
23802394
if (isa<EnumElementDecl>(member))
23812395
Diag.emplace(diagnose(loc, diag::could_not_use_enum_element_on_instance,
23822396
memberName));

lib/Sema/CSSimplify.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2962,7 +2962,16 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
29622962
result.addUnviable(cand, MemberLookupResult::UR_InstanceMemberOnType);
29632963
return;
29642964
}
2965-
2965+
2966+
// If the underlying type of a typealias is fully concrete, it is legal
2967+
// to access the type with a protocol metatype base.
2968+
} else if (isExistential &&
2969+
isa<TypeAliasDecl>(cand) &&
2970+
!cast<TypeAliasDecl>(cand)->getInterfaceType()->getCanonicalType()
2971+
->hasTypeParameter()) {
2972+
2973+
/* We're OK */
2974+
29662975
} else {
29672976
if (!hasStaticMembers) {
29682977
result.addUnviable(cand, MemberLookupResult::UR_TypeMemberOnInstance);

lib/Sema/ConstraintSystem.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,16 @@ ConstraintSystem::getTypeOfMemberReference(
11971197
functionRefKind, locator, base);
11981198
}
11991199

1200+
// Don't open existentials when accessing typealias members of
1201+
// protocols.
1202+
if (auto *alias = dyn_cast<TypeAliasDecl>(value)) {
1203+
if (baseObjTy->isExistentialType()) {
1204+
auto memberTy = alias->getUnderlyingType();
1205+
auto openedType = FunctionType::get(baseObjTy, memberTy);
1206+
return { openedType, memberTy };
1207+
}
1208+
}
1209+
12001210
// Handle associated type lookup as a special case, horribly.
12011211
// FIXME: This is an awful hack.
12021212
if (auto assocType = dyn_cast<AssociatedTypeDecl>(value)) {

lib/Sema/TypeCheckType.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,14 +1228,14 @@ static Type resolveNestedIdentTypeComponent(
12281228

12291229
return ErrorType::get(TC.Context);
12301230
}
1231-
if (auto alias = dyn_cast<TypeAliasDecl>(member)) {
1232-
if (parentTy->isExistentialType() && memberType->hasTypeParameter()) {
1233-
if (diagnoseErrors)
1234-
TC.diagnose(comp->getIdLoc(), diag::typealias_to_assoc_type_outside_of_protocol,
1235-
comp->getIdentifier(), alias->getUnderlyingTypeLoc());
12361231

1237-
return ErrorType::get(TC.Context);
1238-
}
1232+
if (parentTy->isExistentialType() && isa<TypeAliasDecl>(member) &&
1233+
memberType->hasTypeParameter()) {
1234+
if (diagnoseErrors)
1235+
TC.diagnose(comp->getIdLoc(), diag::typealias_outside_of_protocol,
1236+
comp->getIdentifier());
1237+
1238+
return ErrorType::get(TC.Context);
12391239
}
12401240

12411241
// If there are generic arguments, apply them now.

test/decl/typealias/associated_types.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
protocol BaseProto {
44
associatedtype AssocTy
55
}
6-
var a: BaseProto.AssocTy = 4 // expected-error{{cannot use associated type 'AssocTy' outside of its protocol}}
6+
var a: BaseProto.AssocTy = 4
7+
// expected-error@-1{{associated type 'AssocTy' can only be used with a concrete type or generic parameter base}}
78

9+
var a = BaseProto.AssocTy.self
10+
// expected-error@-1{{associated type 'AssocTy' can only be used with a concrete type or generic parameter base}}
811

912
protocol DerivedProto : BaseProto {
1013
func associated() -> AssocTy // no-warning
1114

12-
func existential() -> BaseProto.AssocTy // expected-error{{cannot use associated type 'AssocTy' outside of its protocol}}
15+
func existential() -> BaseProto.AssocTy
16+
// expected-error@-1{{associated type 'AssocTy' can only be used with a concrete type or generic parameter base}}
1317
}
1418

1519

test/decl/typealias/protocol.swift

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,18 @@ protocol P5 {
135135
var a: T2 { get }
136136
}
137137

138+
protocol P6 {
139+
typealias A = Int
140+
typealias B = Self
141+
}
142+
138143
struct T5 : P5 {
139144
// This is OK -- the typealias is fully concrete
140145
var a: P5.T1 // OK
141146

142147
// Invalid -- cannot represent associated type of existential
143-
var v2: P5.T2 // expected-error {{cannot use typealias 'T2' of associated type 'A' outside of its protocol}}
144-
var v3: P5.X // expected-error {{cannot use typealias 'X' of associated type 'Self' outside of its protocol}}
148+
var v2: P5.T2 // expected-error {{typealias 'T2' can only be used with a concrete type or generic parameter base}}
149+
var v3: P5.X // expected-error {{typealias 'X' can only be used with a concrete type or generic parameter base}}
145150

146151
// FIXME: Unqualified reference to typealias from a protocol conformance
147152
var v4: T1 // expected-error {{use of undeclared type 'T1'}}
@@ -150,18 +155,21 @@ struct T5 : P5 {
150155
// Qualified reference
151156
var v6: T5.T1 // OK
152157
var v7: T5.T2 // OK
158+
159+
var v8 = P6.A.self
160+
var v9 = P6.B.self // expected-error {{typealias 'B' can only be used with a concrete type or generic parameter base}}
153161
}
154162

155163
// Unqualified lookup finds typealiases in protocol extensions, though
156-
protocol P6 {
164+
protocol P7 {
157165
associatedtype A
158166
}
159167

160-
extension P6 {
168+
extension P7 {
161169
typealias Y = A
162170
}
163171

164-
struct S6 : P6 {
172+
struct S7 : P7 {
165173
typealias A = Int
166174

167175
// FIXME

0 commit comments

Comments
 (0)