Skip to content

Commit 3ab4c75

Browse files
committed
Sema: ban extensions of known marker protocols
There's no good reason to permit them. Conformances like Copyable and Sendable are pervasive, so it's as though we are permitting extensions of `Any`. Until there's a good argument in favor of such extensions, remove the capability now.
1 parent 056493b commit 3ab4c75

9 files changed

+49
-40
lines changed

include/swift/AST/DiagnosticsSema.def

+2-2
Original file line numberDiff line numberDiff line change
@@ -2537,8 +2537,8 @@ ERROR(invalid_nominal_extension,none,
25372537
(Type, Type))
25382538
NOTE(invalid_extension_rewrite,none,
25392539
"did you mean to extend %0 instead?", (Type))
2540-
ERROR(synthesized_nominal_extension,none,
2541-
"cannot extend synthesized type %0", (Type))
2540+
ERROR(cannot_extend_nominal,none,
2541+
"cannot extend %kind0", (const NominalTypeDecl *))
25422542

25432543
ERROR(retroactive_not_in_extension_inheritance_clause,none,
25442544
"'retroactive' attribute only applies in inheritance clauses in "

lib/Sema/CSBindings.cpp

+3-7
Original file line numberDiff line numberDiff line change
@@ -625,15 +625,11 @@ bool BindingSet::finalize(
625625
for (auto *constraint : *TransitiveProtocols) {
626626
Type protocolTy = constraint->getSecondType();
627627

628-
// The Copyable/Escapable protocols can't have members, yet will be a
629-
// constraint of basically all type variables, so don't suggest it.
630-
//
631-
// NOTE: worth considering for all marker protocols, but keep in
632-
// mind that you're allowed to extend them with members!
628+
// Compiler-known marker protocols cannot be extended with members,
629+
// so do not consider them.
633630
if (auto p = protocolTy->getAs<ProtocolType>()) {
634631
if (ProtocolDecl *decl = p->getDecl())
635-
if (decl->isSpecificProtocol(KnownProtocolKind::Copyable) ||
636-
decl->isSpecificProtocol(KnownProtocolKind::Escapable))
632+
if (decl->getKnownProtocolKind() && decl->isMarkerProtocol())
637633
continue;
638634
}
639635

lib/Sema/TypeCheckDeclPrimary.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -3676,6 +3676,16 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
36763676
return true;
36773677
}
36783678

3679+
/// Compiler-known marker protocols cannot be extended with members.
3680+
static void diagnoseExtensionOfMarkerProtocol(ExtensionDecl *ED) {
3681+
auto *nominal = ED->getExtendedNominal();
3682+
if (auto *proto = dyn_cast_or_null<ProtocolDecl>(nominal)) {
3683+
if (proto->getKnownProtocolKind() && proto->isMarkerProtocol()) {
3684+
ED->diagnose(diag::cannot_extend_nominal, nominal);
3685+
}
3686+
}
3687+
}
3688+
36793689
static void checkTupleExtension(ExtensionDecl *ED) {
36803690
auto *nominal = ED->getExtendedNominal();
36813691
if (!nominal || !isa<BuiltinTupleDecl>(nominal))
@@ -3878,6 +3888,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
38783888
TypeChecker::checkDistributedActor(SF, nominal);
38793889

38803890
diagnoseIncompatibleProtocolsForMoveOnlyType(ED);
3891+
diagnoseExtensionOfMarkerProtocol(ED);
38813892

38823893
checkTupleExtension(ED);
38833894
}

test/Concurrency/sendable_objc_protocol_attr.swift

+2-4
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,11 @@ struct MyStruct: Sendable {
3232
let value: any MyObjCProtocol // Ok
3333
}
3434

35-
extension Sendable {
36-
func compute() {}
37-
}
35+
func compute<T: Sendable>(_ t: T) {}
3836

3937
extension MyObjCProtocol {
4038
func test() {
41-
compute() // Ok
39+
compute(self) // Ok
4240
}
4341
}
4442

test/Constraints/static_members_on_protocol_in_generic_context.swift

+8-13
Original file line numberDiff line numberDiff line change
@@ -350,21 +350,16 @@ func test_leading_dot_syntax_with_typelias() {
350350
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
351351
}
352352

353-
extension Copyable where Self == Int {
354-
static func answer() -> Int { 42 }
355-
}
353+
// User-defined marker protocols do gain the inference for members, since they
354+
// support extensions.
355+
@_marker protocol SomeMarkerProto {}
356+
extension Int: SomeMarkerProto {}
356357

357-
extension Escapable where Self == String {
358-
static func question() -> String { "" }
358+
extension SomeMarkerProto where Self == Int {
359+
static func answer() -> Int { 42 }
359360
}
360361

361362
do {
362-
func testCopyable<T: Copyable>(_: T) {}
363-
func testEscapable<T: Escapable>(_: T) {}
364-
365-
testCopyable(.answer())
366-
// expected-error@-1 {{cannot infer contextual base in reference to member 'answer'}}
367-
368-
testEscapable(.question())
369-
// expected-error@-1 {{cannot infer contextual base in reference to member 'question'}}
363+
func testSomeMarkerProto<T: SomeMarkerProto>(_: T) {}
364+
testSomeMarkerProto(.answer())
370365
}

test/Interpreter/protocol_extensions.swift

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import StdlibUnittest
55

6+
defer { runAllTests() }
67

78
var ProtocolExtensionTestSuite = TestSuite("ProtocolExtensions")
89

@@ -363,4 +364,12 @@ ProtocolExtensionTestSuite.test("WitnessSelf") {
363364
}
364365
}
365366

366-
runAllTests()
367+
@_marker protocol Addable {}
368+
extension Addable {
369+
func increment(this x: Int) -> Int { return x + 100 }
370+
}
371+
extension String: Addable {}
372+
373+
ProtocolExtensionTestSuite.test("MarkerProtocolExtensions") {
374+
expectTrue("hello".increment(this: 11) == 111)
375+
}

test/Sema/copyable.swift

+4
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,7 @@ enum namespace {
2626

2727
func Copyable() -> Copyable { return 0 }
2828
}
29+
30+
extension Copyable { // expected-error {{cannot extend protocol 'Copyable'}}
31+
func hello() {}
32+
}

test/Sema/moveonly_sendable.swift

-13
Original file line numberDiff line numberDiff line change
@@ -150,19 +150,6 @@ func createContainer(_ fd: borrowing FileDescriptor) {
150150
let _: Container<Sendable> = Container(CopyableStruct())
151151
}
152152

153-
func takeTwo<T: Sendable>(_ s1: T, _ s2: T) {}
154-
155-
extension Sendable {
156-
func doIllegalThings() {
157-
return takeTwo(self, self)
158-
}
159-
}
160-
161-
func tryToDupe(_ fd: borrowing FileDescriptor) {
162-
// FIXME: this should describe 'Self' as 'any Sendable' or something.
163-
fd.doIllegalThings() // expected-error {{noncopyable type 'FileDescriptor' cannot be substituted for copyable generic parameter 'Self' in 'Sendable'}}
164-
}
165-
166153
@_moveOnly
167154
struct PaperAirplaneFile {
168155
var fd: FileDescriptor

test/decl/ext/extensions.swift

+9
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,12 @@ extension B4 {
393393
// expected-error@-1 {{extension of existential type 'B4' (aka 'any P4') is not supported}}
394394
// expected-note@-2 {{did you mean to extend 'P4' instead?}} {{11-13=P4}}
395395
}
396+
397+
398+
extension Sendable {} // expected-error {{cannot extend protocol 'Sendable'}}
399+
extension Copyable {} // expected-error {{cannot extend protocol 'Copyable'}}
400+
extension Escapable {} // expected-error {{cannot extend protocol 'Escapable'}}
401+
extension _BitwiseCopyable {} // expected-error {{cannot extend protocol '_BitwiseCopyable'}}
402+
403+
@_marker protocol MyMarkerProto {}
404+
extension MyMarkerProto {} // OK

0 commit comments

Comments
 (0)