Skip to content

Commit 94f0e21

Browse files
authored
Merge pull request #35732 from DougGregor/marker-protocols
Add "marker" protocols, indicated by @_marker.
2 parents 8317df2 + 2493dc9 commit 94f0e21

File tree

13 files changed

+164
-1
lines changed

13 files changed

+164
-1
lines changed

include/swift/AST/ASTMangler.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ class ASTMangler : public Mangler {
6868
std::function<bool (SymbolicReferent)> CanSymbolicReference;
6969

7070
bool canSymbolicReference(SymbolicReferent referent) {
71+
// Marker protocols cannot ever be symbolically referenced.
72+
if (auto nominal = referent.dyn_cast<const NominalTypeDecl *>()) {
73+
if (auto proto = dyn_cast<ProtocolDecl>(nominal)) {
74+
if (proto->isMarkerProtocol())
75+
return false;
76+
}
77+
}
78+
7179
return AllowSymbolicReferences
7280
&& (!CanSymbolicReference || CanSymbolicReference(referent));
7381
}

include/swift/AST/Attr.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,12 @@ SIMPLE_DECL_ATTR(concurrent, Concurrent,
606606
APIBreakingToAdd | APIBreakingToRemove,
607607
107)
608608

609+
SIMPLE_DECL_ATTR(_marker, Marker,
610+
OnProtocol | UserInaccessible |
611+
ABIBreakingToAdd | ABIBreakingToRemove |
612+
APIBreakingToAdd | APIBreakingToRemove,
613+
108)
614+
609615
#undef TYPE_ATTR
610616
#undef DECL_ATTR_ALIAS
611617
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4162,6 +4162,10 @@ class ProtocolDecl final : public NominalTypeDecl {
41624162
ProtocolRethrowsRequirementList getRethrowingRequirements() const;
41634163
bool isRethrowingProtocol() const;
41644164

4165+
/// Determine whether this is a "marker" protocol, meaning that is indicates
4166+
/// semantics but has no corresponding witness table.
4167+
bool isMarkerProtocol() const;
4168+
41654169
private:
41664170
void computeKnownProtocolKind() const;
41674171

include/swift/AST/DiagnosticsSema.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5581,6 +5581,17 @@ NOTE(result_builder_missing_build_limited_availability, none,
55815581
"expression shuffles the elements of this tuple; "
55825582
"this behavior is deprecated", ())
55835583

5584+
//------------------------------------------------------------------------------
5585+
// MARK: marker protocol diagnostics
5586+
//------------------------------------------------------------------------------
5587+
ERROR(marker_protocol_requirement, none,
5588+
"marker protocol %0 cannot have any requirements", (DeclName))
5589+
ERROR(marker_protocol_inherit_nonmarker, none,
5590+
"marker protocol %0 cannot inherit non-marker protocol %1",
5591+
(DeclName, DeclName))
5592+
ERROR(marker_protocol_value,none,
5593+
"marker protocol %0 can only be used in generic constraints", (DeclName))
5594+
55845595
//------------------------------------------------------------------------------
55855596
// MARK: differentiable programming diagnostics
55865597
//------------------------------------------------------------------------------

include/swift/SIL/TypeLowering.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@ class TypeConverter {
813813

814814
/// True if a protocol uses witness tables for dynamic dispatch.
815815
static bool protocolRequiresWitnessTable(ProtocolDecl *P) {
816+
if (P->isMarkerProtocol())
817+
return false;
818+
816819
return swift::protocolRequiresWitnessTable(getProtocolDispatchStrategy(P));
817820
}
818821

lib/AST/Decl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4601,6 +4601,10 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc,
46014601
setTrailingWhereClause(TrailingWhere);
46024602
}
46034603

4604+
bool ProtocolDecl::isMarkerProtocol() const {
4605+
return getAttrs().hasAttribute<MarkerAttr>();
4606+
}
4607+
46044608
ArrayRef<ProtocolDecl *> ProtocolDecl::getInheritedProtocols() const {
46054609
auto *mutThis = const_cast<ProtocolDecl *>(this);
46064610
return evaluateOrDefault(getASTContext().evaluator,

lib/IRGen/GenMeta.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5150,6 +5150,10 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
51505150
void IRGenModule::emitProtocolDecl(ProtocolDecl *protocol) {
51515151
PrettyStackTraceDecl stackTraceRAII("emitting metadata for", protocol);
51525152

5153+
// Marker protocols are never emitted.
5154+
if (protocol->isMarkerProtocol())
5155+
return;
5156+
51535157
// Emit remote reflection metadata for the protocol.
51545158
emitFieldDescriptor(protocol);
51555159

@@ -5239,6 +5243,11 @@ GenericRequirementsMetadata irgen::addGenericRequirements(
52395243
case RequirementKind::Conformance: {
52405244
auto protocol = requirement.getSecondType()->castTo<ProtocolType>()
52415245
->getDecl();
5246+
5247+
// Marker protocols do not record generic requirements at all.
5248+
if (protocol->isMarkerProtocol())
5249+
break;
5250+
52425251
bool needsWitnessTable =
52435252
Lowering::TypeConverter::protocolRequiresWitnessTable(protocol);
52445253
auto flags = GenericRequirementFlags(GenericRequirementKind::Protocol,

lib/Sema/TypeCheckAttr.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,35 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
391391
}
392392
}
393393
}
394+
395+
void visitMarkerAttr(MarkerAttr *attr) {
396+
auto proto = dyn_cast<ProtocolDecl>(D);
397+
if (!proto)
398+
return;
399+
400+
// A marker protocol cannot inherit a non-marker protocol.
401+
for (auto inheritedProto : proto->getInheritedProtocols()) {
402+
if (!inheritedProto->isMarkerProtocol()) {
403+
proto->diagnose(
404+
diag::marker_protocol_inherit_nonmarker,
405+
proto->getName(), inheritedProto->getName());
406+
inheritedProto->diagnose(
407+
diag::decl_declared_here, inheritedProto->getName());
408+
}
409+
}
410+
411+
// A marker protocol cannot have any requirements.
412+
for (auto member : proto->getAllMembers()) {
413+
auto value = dyn_cast<ValueDecl>(member);
414+
if (!value)
415+
continue;
416+
417+
if (value->isProtocolRequirement()) {
418+
value->diagnose(diag::marker_protocol_requirement, proto->getName());
419+
break;
420+
}
421+
}
422+
}
394423
};
395424
} // end anonymous namespace
396425

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,7 @@ namespace {
15341534
UNINTERESTING_ATTR(Concurrent)
15351535

15361536
UNINTERESTING_ATTR(AtRethrows)
1537+
UNINTERESTING_ATTR(Marker)
15371538
#undef UNINTERESTING_ATTR
15381539

15391540
void visitAvailableAttr(AvailableAttr *attr) {

lib/Sema/TypeCheckType.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3918,6 +3918,12 @@ class UnsupportedProtocolVisitor
39183918
proto->getName());
39193919
T->setInvalid();
39203920
}
3921+
if (proto->isMarkerProtocol()) {
3922+
Ctx.Diags.diagnose(comp->getNameLoc(),
3923+
diag::marker_protocol_value,
3924+
proto->getName());
3925+
T->setInvalid();
3926+
}
39213927
} else if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(comp->getBoundDecl())) {
39223928
auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType());
39233929
type.findIf([&](Type type) -> bool {
@@ -3928,6 +3934,14 @@ class UnsupportedProtocolVisitor
39283934
for (auto *proto : layout.getProtocols()) {
39293935
auto *protoDecl = proto->getDecl();
39303936

3937+
if (protoDecl->isMarkerProtocol()) {
3938+
Ctx.Diags.diagnose(comp->getNameLoc(),
3939+
diag::marker_protocol_value,
3940+
protoDecl->getName());
3941+
T->setInvalid();
3942+
continue;
3943+
}
3944+
39313945
if (protoDecl->existentialTypeSupported())
39323946
continue;
39333947

lib/TBDGen/TBDGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,7 @@ static bool isValidProtocolMemberForTBDGen(const Decl *D) {
10371037
#endif
10381038

10391039
void TBDGenVisitor::visitProtocolDecl(ProtocolDecl *PD) {
1040-
if (!PD->isObjC()) {
1040+
if (!PD->isObjC() && !PD->isMarkerProtocol()) {
10411041
addSymbol(LinkEntity::forProtocolDescriptor(PD));
10421042

10431043
struct WitnessVisitor : public SILWitnessVisitor<WitnessVisitor> {

test/IRGen/marker_protocol.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-swift-frontend -primary-file %s -emit-ir -o - | %FileCheck %s
2+
3+
// Marker protocols should have no ABI impact at all, so this source file checks
4+
// for the absence of symbols related to marker protocols.
5+
6+
// CHECK-NOT: $s15marker_protocol1PP
7+
// CHECK-NOT: $s15marker_protocol1PMp
8+
9+
@_marker public protocol P { }
10+
11+
extension Int: P { }
12+
extension Array: P where Element: P { }
13+
14+
// Note: no witness tables
15+
// CHECK: swiftcc void @"$s15marker_protocol7genericyyxAA1PRzlF"(%swift.opaque* noalias nocapture %0, %swift.type* %T)
16+
public func generic<T: P>(_: T) { }
17+
18+
public func testGeneric(i: Int, array: [Int]) {
19+
generic(i)
20+
generic(array)
21+
}
22+
23+
public protocol Q: P { }

test/attr/attr_marker_protocol.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// Marker protocol definition
4+
@_marker protocol P1 {
5+
func f() // expected-error{{marker protocol 'P1' cannot have any requirements}}
6+
}
7+
8+
@_marker protocol P2 {
9+
associatedtype AT // expected-error{{marker protocol 'P2' cannot have any requirements}}
10+
}
11+
12+
@_marker protocol P3 {
13+
typealias A = Int
14+
}
15+
16+
protocol P4 { } // expected-note{{'P4' declared here}}
17+
18+
@_marker protocol P5: P4 { } // expected-error{{marker protocol 'P5' cannot inherit non-marker protocol 'P4'}}
19+
20+
// Legitimate uses of marker protocols.
21+
extension P3 {
22+
func f() { }
23+
}
24+
25+
extension Int: P3 { }
26+
extension Array: P3 where Element: P3 { } // expected-note{{requirement from conditional conformance of '[Double]' to 'P3'}}
27+
28+
protocol P6: P3 { } // okay
29+
@_marker protocol P7: P3 { } // okay
30+
31+
func genericOk<T: P3>(_: T) { }
32+
33+
func testGenericOk(i: Int, arr: [Int], nope: [Double]) {
34+
genericOk(i)
35+
genericOk(arr)
36+
genericOk(nope) // expected-error{{global function 'genericOk' requires that 'Double' conform to 'P3'}}
37+
}
38+
39+
// Incorrect uses of marker protocols in types.
40+
func testNotOkay(a: Any) {
41+
var mp1: P3 = 17 // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
42+
_ = mp1
43+
mp1 = 17
44+
45+
if let mp2 = a as? P3 { _ = mp2 } // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
46+
if let mp3 = a as? AnyObject & P3 { _ = mp3 } // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
47+
if a is AnyObject & P3 { } // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
48+
49+
func inner(p3: P3) { } // expected-error{{marker protocol 'P3' can only be used in generic constraints}}
50+
}
51+

0 commit comments

Comments
 (0)