Skip to content

Commit 204d025

Browse files
committed
[SE-0404] Allow protocols to be nested in non-generic contexts
1 parent a66f28e commit 204d025

File tree

17 files changed

+470
-94
lines changed

17 files changed

+470
-94
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,9 +2187,12 @@ ERROR(unsupported_type_nested_in_protocol,none,
21872187
ERROR(unsupported_type_nested_in_protocol_extension,none,
21882188
"type %0 cannot be nested in protocol extension of %1",
21892189
(const NominalTypeDecl *, const ProtocolDecl *))
2190-
ERROR(unsupported_nested_protocol,none,
2191-
"protocol %0 cannot be nested inside another declaration",
2190+
ERROR(unsupported_nested_protocol_in_generic,none,
2191+
"protocol %0 cannot be nested in a generic context",
21922192
(const NominalTypeDecl *))
2193+
ERROR(unsupported_nested_protocol_in_protocol,none,
2194+
"protocol %0 cannot be nested in protocol %1",
2195+
(const NominalTypeDecl *, const NominalTypeDecl *))
21932196
ERROR(where_nongeneric_ctx,none,
21942197
"'where' clause on non-generic member declaration requires a "
21952198
"generic context", ())

include/swift/AST/TypeMemberVisitor.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ class TypeMemberVisitor : public DeclVisitor<ImplClass, RetTy> {
3737
}
3838
BAD_MEMBER(Extension)
3939
BAD_MEMBER(Import)
40-
BAD_MEMBER(Protocol)
4140
BAD_MEMBER(TopLevelCode)
4241
BAD_MEMBER(Operator)
4342
BAD_MEMBER(PrecedenceGroup)

lib/AST/ASTScopeLookup.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,7 @@ void ASTScopeImpl::lookup(const NullablePtr<const ASTScopeImpl> limit,
193193
consumer.startingNextLookupStep();
194194
#endif
195195

196-
// Certain illegal nestings, e.g. protocol nestled inside a struct,
197-
// require that lookup stop at the outer scope.
196+
// Certain illegal nestings require that lookup stop at the outer scope.
198197
if (this == limit.getPtrOrNull()) {
199198
#ifndef NDEBUG
200199
consumer.finishingLookup("limit return");
@@ -558,13 +557,13 @@ GenericTypeOrExtensionScope::getLookupLimitForDecl() const {
558557

559558
NullablePtr<const ASTScopeImpl>
560559
NominalTypeScope::getLookupLimitForDecl() const {
561-
if (isa<ProtocolDecl>(decl)) {
562-
// ProtocolDecl can only be legally nested in a SourceFile,
563-
// so any other kind of Decl is illegal
564-
return parentIfNotChildOfTopScope();
565-
}
566-
// AFAICT, a struct, decl, or enum can be nested inside anything
567-
// but a ProtocolDecl.
560+
// If a protocol is (invalidly) nested in a generic context,
561+
// do not look in to those outer generic contexts,
562+
// as types found there may contain implicitly inferred generic parameters.
563+
if (isa<ProtocolDecl>(decl) && decl->getDeclContext()->isGenericContext())
564+
return getLookupParent();
565+
566+
// Otherwise, nominals can be nested inside anything but a ProtocolDecl.
568567
return ancestorWithDeclSatisfying(
569568
[&](const Decl *const d) { return isa<ProtocolDecl>(d); });
570569
}

lib/AST/Decl.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4868,11 +4868,10 @@ static Type computeNominalType(NominalTypeDecl *decl, DeclTypeKind kind) {
48684868
// If `decl` is a nested type, find the parent type.
48694869
Type ParentTy;
48704870
DeclContext *dc = decl->getDeclContext();
4871-
bool isObjCProtocol = isa<ProtocolDecl>(decl) && decl->hasClangNode();
4871+
bool isUnsupportedNestedProtocol =
4872+
isa<ProtocolDecl>(decl) && decl->getParent()->isGenericContext();
48724873

4873-
// Objective-C protocols, unlike Swift protocols, could be nested
4874-
// in other types.
4875-
if ((isObjCProtocol || !isa<ProtocolDecl>(decl)) && dc->isTypeContext()) {
4874+
if (!isUnsupportedNestedProtocol && dc->isTypeContext()) {
48764875
switch (kind) {
48774876
case DeclTypeKind::DeclaredType: {
48784877
if (auto *nominal = dc->getSelfNominalTypeDecl())
@@ -4909,9 +4908,9 @@ static Type computeNominalType(NominalTypeDecl *decl, DeclTypeKind kind) {
49094908
}
49104909

49114910
llvm_unreachable("Unhandled DeclTypeKind in switch.");
4912-
} else {
4913-
return NominalType::get(decl, ParentTy, ctx);
49144911
}
4912+
4913+
return NominalType::get(decl, ParentTy, ctx);
49154914
}
49164915

49174916
Type NominalTypeDecl::getDeclaredType() const {

lib/AST/DeclContext.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ void DeclContext::forEachGenericContext(
132132
if (auto genericCtx = decl->getAsGenericContext())
133133
if (auto *gpList = genericCtx->getGenericParams())
134134
fn(gpList);
135+
136+
// Protocols do not capture outer generic parameters.
137+
if (isa<ProtocolDecl>(decl))
138+
return;
135139
}
136140
} while ((dc = dc->getParentForLookup()));
137141
}
@@ -259,12 +263,18 @@ DeclContext *DeclContext::getInnermostSkippedFunctionContext() {
259263
}
260264

261265
DeclContext *DeclContext::getParentForLookup() const {
262-
if (isa<ProtocolDecl>(this) || isa<ExtensionDecl>(this)) {
263-
// If we are inside a protocol or an extension, skip directly
266+
if (isa<ExtensionDecl>(this)) {
267+
// If we are inside an extension, skip directly
264268
// to the module scope context, without looking at any (invalid)
265269
// outer types.
266270
return getModuleScopeContext();
267271
}
272+
if (isa<ProtocolDecl>(this) && getParent()->isGenericContext()) {
273+
// Protocols in generic contexts must not look in to their parents,
274+
// as the parents may contain types with inferred implicit
275+
// generic parameters not present in the protocol's generic signature.
276+
return getModuleScopeContext();
277+
}
268278
if (isa<NominalTypeDecl>(this)) {
269279
// If we are inside a nominal type that is inside a protocol,
270280
// skip the protocol.

lib/IRGen/GenDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5530,7 +5530,6 @@ void IRGenModule::emitNestedTypeDecls(DeclRange members) {
55305530
switch (member->getKind()) {
55315531
case DeclKind::Import:
55325532
case DeclKind::TopLevelCode:
5533-
case DeclKind::Protocol:
55345533
case DeclKind::Extension:
55355534
case DeclKind::InfixOperator:
55365535
case DeclKind::PrefixOperator:
@@ -5586,6 +5585,9 @@ void IRGenModule::emitNestedTypeDecls(DeclRange members) {
55865585
case DeclKind::Class:
55875586
emitClassDecl(cast<ClassDecl>(member));
55885587
continue;
5588+
case DeclKind::Protocol:
5589+
emitProtocolDecl(cast<ProtocolDecl>(member));
5590+
continue;
55895591
case DeclKind::MacroExpansion:
55905592
// Expansion already visited as auxiliary decls.
55915593
continue;

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9241,7 +9241,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) {
92419241
ProtocolDecl(CurDeclContext, ProtocolLoc, NameLoc, ProtocolName,
92429242
Context.AllocateCopy(PrimaryAssociatedTypeNames),
92439243
Context.AllocateCopy(InheritedProtocols), TrailingWhere);
9244-
// No need to setLocalDiscriminator: protocols can't appear in local contexts.
9244+
recordLocalType(Proto);
92459245

92469246
Proto->getAttrs() = Attributes;
92479247
if (whereClauseHadCodeCompletion && CodeCompletionCallbacks)

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2675,10 +2675,15 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26752675
return;
26762676
}
26772677

2678-
// We don't support protocols outside the top level of a file.
2679-
if (isa<ProtocolDecl>(NTD) &&
2680-
!DC->isModuleScopeContext()) {
2681-
NTD->diagnose(diag::unsupported_nested_protocol, NTD);
2678+
// We don't support protocols nested in generic contexts.
2679+
// This includes protocols nested in other protocols.
2680+
if (isa<ProtocolDecl>(NTD) && DC->isGenericContext()) {
2681+
if (auto *OuterPD = DC->getSelfProtocolDecl())
2682+
NTD->diagnose(diag::unsupported_nested_protocol_in_protocol, NTD,
2683+
OuterPD);
2684+
else
2685+
NTD->diagnose(diag::unsupported_nested_protocol_in_generic, NTD);
2686+
26822687
NTD->setInvalid();
26832688
return;
26842689
}
@@ -2691,6 +2696,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26912696
} else {
26922697
NTD->diagnose(diag::unsupported_type_nested_in_protocol, NTD, proto);
26932698
}
2699+
NTD->setInvalid();
26942700
}
26952701
}
26962702

@@ -2704,6 +2710,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
27042710
} else {
27052711
NTD->diagnose(diag::unsupported_type_nested_in_generic_closure, NTD);
27062712
}
2713+
NTD->setInvalid();
27072714
}
27082715
}
27092716
}

test/IRGen/protocol_metadata.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,60 @@ protocol Comprehensive {
118118
// CHECK-SAME: %swift.protocol_requirement { i32 4, i32 0 },
119119
// CHECK-SAME: %swift.protocol_requirement { i32 6, i32 0 }
120120

121+
struct ParentType {
122+
123+
// NESTED: [[NESTED_NAME:@.*]] = private constant [7 x i8] c"Nested\00"
124+
// NESTED: [[NESTED_RETURNVALUE_NAME:@.*]] = private constant [12 x i8] c"ReturnValue\00"
125+
126+
// NESTED: @"$s17protocol_metadata10ParentTypeV6NestedMp" = hidden constant
127+
// NESTED-SAME: i32 65603,
128+
// NESTED-SAME: @"$s17protocol_metadata10ParentTypeVMn"
129+
// NESTED-SAME: @"$s17protocol_metadata10ParentTypeV6NestedMp", i32 0, i32 1)
130+
// NESTED-SAME: [7 x i8]* [[NESTED_NAME]]
131+
// NESTED-SAME: @"$s17protocol_metadata10ParentTypeV6NestedMp", i32 0, i32 2)
132+
// NESTED-SAME: i32 0,
133+
// NESTED-SAME: i32 2,
134+
// NESTED-SAME: [12 x i8]* [[NESTED_RETURNVALUE_NAME]]
135+
// NESTED-SAME: @"$s17protocol_metadata10ParentTypeV6NestedMp", i32 0, i32 5)
136+
// NESTED-SAME: %swift.protocol_requirement { i32 7, i32 0 },
137+
// NESTED-SAME: %swift.protocol_requirement { i32 17, i32 0 }
138+
protocol Nested {
139+
associatedtype ReturnValue
140+
func doSomething() -> ReturnValue
141+
}
142+
}
143+
144+
extension ParentType {
145+
146+
// NESTED: [[NESTEDVIAEXT_NAME:@.*]] = private constant [19 x i8] c"NestedViaExtension\00"
147+
148+
// NESTED: @"$s17protocol_metadata10ParentTypeV18NestedViaExtensionMp" = hidden constant
149+
// NESTED-SAME: i32 65603,
150+
// NESTED-SAME: @"$s17protocol_metadata10ParentTypeVMn"
151+
// NESTED-SAME: @"$s17protocol_metadata10ParentTypeV18NestedViaExtensionMp", i32 0, i32 1)
152+
// NESTED-SAME: [19 x i8]* [[NESTEDVIAEXT_NAME]]
153+
// NESTED-SAME: @"$s17protocol_metadata10ParentTypeV18NestedViaExtensionMp", i32 0, i32 2)
154+
// NESTED-SAME: i32 0,
155+
// NESTED-SAME: i32 1,
156+
// NESTED-SAME: i32 0,
157+
// NESTED-SAME: %swift.protocol_requirement { i32 17, i32 0 }
158+
protocol NestedViaExtension {
159+
func foo()
160+
}
161+
}
162+
163+
func parentFunc() {
164+
// NESTED: @"$s17protocol_metadata10parentFuncyyF6NestedL_Mp" = internal constant
165+
// NESTED-SAME: i32 65603,
166+
// NESTED-SAME: @"$s17protocol_metadata10parentFuncyyF6NestedL_PMXX"
167+
// NESTED-SAME: @"$s17protocol_metadata10parentFuncyyF6NestedL_Mp", i32 0, i32 1)
168+
// NESTED-SAME: [7 x i8]* [[NESTED_NAME]]
169+
// NESTED-SAME: @"$s17protocol_metadata10parentFuncyyF6NestedL_Mp", i32 0, i32 2)
170+
// NESTED-SAME: i32 0,
171+
// NESTED-SAME: i32 1,
172+
// NESTED-SAME: i32 0,
173+
// NESTED-SAME: %swift.protocol_requirement { i32 17, i32 0 }
174+
protocol Nested {
175+
func foo()
176+
}
177+
}

test/PrintAsObjC/classes.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,12 @@ class MyObject : NSObject {}
409409
@objc @objcMembers class DeeperIn {}
410410
}
411411

412+
// CHECK-LABEL: SWIFT_CLASS_NAMED("CustomNameInner")
413+
// CHECK-NEXT: @interface MyInnerClass
414+
// CHECK-NEXT: init
415+
// CHECK-NEXT: @end
416+
@objc(MyInnerClass) @objcMembers class CustomNameInner {}
417+
412418
// CHECK-LABEL: @interface AnotherInner : A1
413419
// CHECK-NEXT: init
414420
// CHECK-NEXT: @end

test/PrintAsObjC/protocols.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,26 @@ extension NSString : A, ZZZ {}
137137
@objc optional func f()
138138
}
139139

140+
// NESTED-LABEL: @interface ParentClass
141+
// NESTED-NEXT: @end
142+
@objc class ParentClass {
143+
144+
// NESTED-LABEL: @protocol Nested
145+
// NESTED-NEXT: @end
146+
@objc protocol Nested {}
147+
148+
// NESTED-LABEL: SWIFT_PROTOCOL_NAMED("Nested2")
149+
// NESTED-NEXT: @protocol NestedInParent
150+
// NESTED-NEXT: @end
151+
@objc(NestedInParent) protocol Nested2 {}
152+
}
153+
154+
extension ParentClass {
155+
// NESTED-LABEL: @protocol NestedInExtensionOfParent
156+
// NESTED-NEXT: @end
157+
@objc protocol NestedInExtensionOfParent {}
158+
}
159+
140160
// NEGATIVE-NOT: @protocol PrivateProto
141161
@objc private protocol PrivateProto {}
142162

test/Runtime/nested-protocols.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %target-run-simple-swift
2+
3+
// REQUIRES: executable_test
4+
5+
enum E {
6+
protocol P {}
7+
}
8+
9+
struct Conforms {}
10+
extension Conforms: E.P {}
11+
12+
struct DoesNotConform {}
13+
14+
struct S<T> {}
15+
extension S: E.P where T: E.P {}
16+
17+
@inline(never)
18+
func castToNested(_ value: Any) -> (any E.P)? {
19+
value as? any E.P
20+
}
21+
22+
// Regular conformance.
23+
24+
precondition(castToNested(Conforms()) != nil)
25+
precondition(castToNested(DoesNotConform()) == nil)
26+
27+
// Conditional conformance.
28+
29+
precondition(castToNested(S<Conforms>()) != nil)
30+
precondition(castToNested(S<DoesNotConform>()) == nil)
31+
32+
// Verify that 'E.P' and a non-nested 'P' are different.
33+
34+
protocol P {}
35+
36+
@inline(never)
37+
func castToNonNested(_ value: Any) -> (any P)? {
38+
value as? any P
39+
}
40+
41+
precondition(castToNonNested(Conforms()) == nil)
42+
precondition(castToNonNested(S<Conforms>()) == nil)
43+
precondition(castToNonNested(DoesNotConform()) == nil)
44+
precondition(castToNonNested(S<DoesNotConform>()) == nil)
45+
46+
// Local protocols.
47+
48+
@inline(never)
49+
func f0(_ input: Any) -> (Any, inputConforms: Bool) {
50+
51+
protocol LocalProto { }
52+
struct ConformsToLocal: LocalProto {}
53+
54+
if let input = input as? any LocalProto {
55+
return (input, true)
56+
} else {
57+
return (ConformsToLocal(), false)
58+
}
59+
}
60+
61+
var result = f0(DoesNotConform())
62+
precondition(result.inputConforms == false)
63+
result = f0(result.0)
64+
precondition(result.inputConforms == true)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public struct Outer {
2+
public protocol Inner {
3+
func foo()
4+
}
5+
}
6+
7+
extension Outer {
8+
public protocol InnerInExtension {
9+
func bar()
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -module-name nestedprotocolsource -emit-module-path %t/nestedprotocolsource.swiftmodule -primary-file %S/Inputs/nested-protocols.swift
3+
// RUN: %target-swift-frontend -emit-sil %s -I %t | %FileCheck %s
4+
import nestedprotocolsource
5+
6+
// CHECK: usesNested<A>(_:)
7+
// CHECK-NEXT: sil @$s4main10usesNestedyyx20nestedprotocolsource5OuterV5InnerRzlF :
8+
public func usesNested(_ x: some Outer.Inner) {}
9+
10+
// CHECK: usesNestedInExtension<A>(_:)
11+
// CHECK-NEXT: sil @$s4main21usesNestedInExtensionyyx20nestedprotocolsource5OuterV05InnerdE0RzlF :
12+
public func usesNestedInExtension(_ x: some Outer.InnerInExtension) {}

test/decl/inherit/initializer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func testClassInGenericFunc<T>(t: T) {
107107
class A { init(t: T) {} } // expected-error {{type 'A' cannot be nested in generic function 'testClassInGenericFunc(t:)'}}
108108
class B : A {} // expected-error {{type 'B' cannot be nested in generic function 'testClassInGenericFunc(t:)'}}
109109

110-
_ = B(t: t)
110+
_ = B(t: t) // expected-error {{'B' cannot be constructed because it has no accessible initializers}}
111111
}
112112

113113

0 commit comments

Comments
 (0)