Skip to content

Commit c9d66f0

Browse files
committed
Add "marker" protocols, indicated by @_marker.
A marker protocol is a protocol with no requirements and with no ABI footprint. They can be used to indicate semantics, only, and one can never have values of a marker protocol type. We still mangle marker protocols when they are used as generic requirements, because they are a distinguishing characteristic for overloading, but "no ABI footprint" means no protocol descriptors, conformance descriptors, or dynamic discovery of any kind.
1 parent d12b390 commit c9d66f0

File tree

11 files changed

+149
-0
lines changed

11 files changed

+149
-0
lines changed

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: 4 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

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

test/IRGen/marker_protocol.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
// CHECK-NOT: "\01l_protocols"
9+
10+
@_marker public protocol P { }
11+
12+
extension Int: P { }
13+
extension Array: P where Element: P { }
14+
15+
// Note: no witness tables
16+
// CHECK: define swiftcc void @"$s15marker_protocol7genericyyxAA1PRzlF"(%swift.opaque* noalias nocapture %0, %swift.type* %T)
17+
public func generic<T: P>(_: T) { }
18+
19+
public func testGeneric(i: Int, array: [Int]) {
20+
generic(i)
21+
generic(array)
22+
}

0 commit comments

Comments
 (0)