diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 1247da61d3697..92ce8186bcfb6 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -68,6 +68,14 @@ class ASTMangler : public Mangler { std::function CanSymbolicReference; bool canSymbolicReference(SymbolicReferent referent) { + // Marker protocols cannot ever be symbolically referenced. + if (auto nominal = referent.dyn_cast()) { + if (auto proto = dyn_cast(nominal)) { + if (proto->isMarkerProtocol()) + return false; + } + } + return AllowSymbolicReferences && (!CanSymbolicReference || CanSymbolicReference(referent)); } diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 0320595f92d7c..430af4e8926bd 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -606,6 +606,12 @@ SIMPLE_DECL_ATTR(concurrent, Concurrent, APIBreakingToAdd | APIBreakingToRemove, 107) +SIMPLE_DECL_ATTR(_marker, Marker, + OnProtocol | UserInaccessible | + ABIBreakingToAdd | ABIBreakingToRemove | + APIBreakingToAdd | APIBreakingToRemove, + 108) + #undef TYPE_ATTR #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c7f924a5b9042..322140b3c7703 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4162,6 +4162,10 @@ class ProtocolDecl final : public NominalTypeDecl { ProtocolRethrowsRequirementList getRethrowingRequirements() const; bool isRethrowingProtocol() const; + /// Determine whether this is a "marker" protocol, meaning that is indicates + /// semantics but has no corresponding witness table. + bool isMarkerProtocol() const; + private: void computeKnownProtocolKind() const; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 065fa3c118cdc..7ac2a9c04f845 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5581,6 +5581,17 @@ NOTE(result_builder_missing_build_limited_availability, none, "expression shuffles the elements of this tuple; " "this behavior is deprecated", ()) +//------------------------------------------------------------------------------ +// MARK: marker protocol diagnostics +//------------------------------------------------------------------------------ +ERROR(marker_protocol_requirement, none, + "marker protocol %0 cannot have any requirements", (DeclName)) +ERROR(marker_protocol_inherit_nonmarker, none, + "marker protocol %0 cannot inherit non-marker protocol %1", + (DeclName, DeclName)) +ERROR(marker_protocol_value,none, + "marker protocol %0 can only be used in generic constraints", (DeclName)) + //------------------------------------------------------------------------------ // MARK: differentiable programming diagnostics //------------------------------------------------------------------------------ diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index f9e5883d85769..15cf46262564c 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -813,6 +813,9 @@ class TypeConverter { /// True if a protocol uses witness tables for dynamic dispatch. static bool protocolRequiresWitnessTable(ProtocolDecl *P) { + if (P->isMarkerProtocol()) + return false; + return swift::protocolRequiresWitnessTable(getProtocolDispatchStrategy(P)); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 109cf7d1bbfa1..7b80409f06007 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4601,6 +4601,10 @@ ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, setTrailingWhereClause(TrailingWhere); } +bool ProtocolDecl::isMarkerProtocol() const { + return getAttrs().hasAttribute(); +} + ArrayRef ProtocolDecl::getInheritedProtocols() const { auto *mutThis = const_cast(this); return evaluateOrDefault(getASTContext().evaluator, diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index f34a70ec729af..8411c61ebc8a3 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5150,6 +5150,10 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { void IRGenModule::emitProtocolDecl(ProtocolDecl *protocol) { PrettyStackTraceDecl stackTraceRAII("emitting metadata for", protocol); + // Marker protocols are never emitted. + if (protocol->isMarkerProtocol()) + return; + // Emit remote reflection metadata for the protocol. emitFieldDescriptor(protocol); @@ -5239,6 +5243,11 @@ GenericRequirementsMetadata irgen::addGenericRequirements( case RequirementKind::Conformance: { auto protocol = requirement.getSecondType()->castTo() ->getDecl(); + + // Marker protocols do not record generic requirements at all. + if (protocol->isMarkerProtocol()) + break; + bool needsWitnessTable = Lowering::TypeConverter::protocolRequiresWitnessTable(protocol); auto flags = GenericRequirementFlags(GenericRequirementKind::Protocol, diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 2b203dbba4d0d..25436b86d0b65 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -391,6 +391,35 @@ class AttributeChecker : public AttributeVisitor { } } } + + void visitMarkerAttr(MarkerAttr *attr) { + auto proto = dyn_cast(D); + if (!proto) + return; + + // A marker protocol cannot inherit a non-marker protocol. + for (auto inheritedProto : proto->getInheritedProtocols()) { + if (!inheritedProto->isMarkerProtocol()) { + proto->diagnose( + diag::marker_protocol_inherit_nonmarker, + proto->getName(), inheritedProto->getName()); + inheritedProto->diagnose( + diag::decl_declared_here, inheritedProto->getName()); + } + } + + // A marker protocol cannot have any requirements. + for (auto member : proto->getAllMembers()) { + auto value = dyn_cast(member); + if (!value) + continue; + + if (value->isProtocolRequirement()) { + value->diagnose(diag::marker_protocol_requirement, proto->getName()); + break; + } + } + } }; } // end anonymous namespace diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 18c58e748efbf..35975bc4f7547 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1534,6 +1534,7 @@ namespace { UNINTERESTING_ATTR(Concurrent) UNINTERESTING_ATTR(AtRethrows) + UNINTERESTING_ATTR(Marker) #undef UNINTERESTING_ATTR void visitAvailableAttr(AvailableAttr *attr) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 6cb38a618538b..e75dd666ec21b 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3918,6 +3918,12 @@ class UnsupportedProtocolVisitor proto->getName()); T->setInvalid(); } + if (proto->isMarkerProtocol()) { + Ctx.Diags.diagnose(comp->getNameLoc(), + diag::marker_protocol_value, + proto->getName()); + T->setInvalid(); + } } else if (auto *alias = dyn_cast_or_null(comp->getBoundDecl())) { auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType()); type.findIf([&](Type type) -> bool { @@ -3928,6 +3934,14 @@ class UnsupportedProtocolVisitor for (auto *proto : layout.getProtocols()) { auto *protoDecl = proto->getDecl(); + if (protoDecl->isMarkerProtocol()) { + Ctx.Diags.diagnose(comp->getNameLoc(), + diag::marker_protocol_value, + protoDecl->getName()); + T->setInvalid(); + continue; + } + if (protoDecl->existentialTypeSupported()) continue; diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 1e7aca24a138a..2963ad6dac752 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -1037,7 +1037,7 @@ static bool isValidProtocolMemberForTBDGen(const Decl *D) { #endif void TBDGenVisitor::visitProtocolDecl(ProtocolDecl *PD) { - if (!PD->isObjC()) { + if (!PD->isObjC() && !PD->isMarkerProtocol()) { addSymbol(LinkEntity::forProtocolDescriptor(PD)); struct WitnessVisitor : public SILWitnessVisitor { diff --git a/test/IRGen/marker_protocol.swift b/test/IRGen/marker_protocol.swift new file mode 100644 index 0000000000000..afe3b47c64e40 --- /dev/null +++ b/test/IRGen/marker_protocol.swift @@ -0,0 +1,23 @@ +// RUN: %target-swift-frontend -primary-file %s -emit-ir -o - | %FileCheck %s + +// Marker protocols should have no ABI impact at all, so this source file checks +// for the absence of symbols related to marker protocols. + +// CHECK-NOT: $s15marker_protocol1PP +// CHECK-NOT: $s15marker_protocol1PMp + +@_marker public protocol P { } + +extension Int: P { } +extension Array: P where Element: P { } + +// Note: no witness tables +// CHECK: swiftcc void @"$s15marker_protocol7genericyyxAA1PRzlF"(%swift.opaque* noalias nocapture %0, %swift.type* %T) +public func generic(_: T) { } + +public func testGeneric(i: Int, array: [Int]) { + generic(i) + generic(array) +} + +public protocol Q: P { } diff --git a/test/attr/attr_marker_protocol.swift b/test/attr/attr_marker_protocol.swift new file mode 100644 index 0000000000000..e2a8928fbf29a --- /dev/null +++ b/test/attr/attr_marker_protocol.swift @@ -0,0 +1,51 @@ +// RUN: %target-typecheck-verify-swift + +// Marker protocol definition +@_marker protocol P1 { + func f() // expected-error{{marker protocol 'P1' cannot have any requirements}} +} + +@_marker protocol P2 { + associatedtype AT // expected-error{{marker protocol 'P2' cannot have any requirements}} +} + +@_marker protocol P3 { + typealias A = Int +} + +protocol P4 { } // expected-note{{'P4' declared here}} + +@_marker protocol P5: P4 { } // expected-error{{marker protocol 'P5' cannot inherit non-marker protocol 'P4'}} + +// Legitimate uses of marker protocols. +extension P3 { + func f() { } +} + +extension Int: P3 { } +extension Array: P3 where Element: P3 { } // expected-note{{requirement from conditional conformance of '[Double]' to 'P3'}} + +protocol P6: P3 { } // okay +@_marker protocol P7: P3 { } // okay + +func genericOk(_: T) { } + +func testGenericOk(i: Int, arr: [Int], nope: [Double]) { + genericOk(i) + genericOk(arr) + genericOk(nope) // expected-error{{global function 'genericOk' requires that 'Double' conform to 'P3'}} +} + +// Incorrect uses of marker protocols in types. +func testNotOkay(a: Any) { + var mp1: P3 = 17 // expected-error{{marker protocol 'P3' can only be used in generic constraints}} + _ = mp1 + mp1 = 17 + + if let mp2 = a as? P3 { _ = mp2 } // expected-error{{marker protocol 'P3' can only be used in generic constraints}} + if let mp3 = a as? AnyObject & P3 { _ = mp3 } // expected-error{{marker protocol 'P3' can only be used in generic constraints}} + if a is AnyObject & P3 { } // expected-error{{marker protocol 'P3' can only be used in generic constraints}} + + func inner(p3: P3) { } // expected-error{{marker protocol 'P3' can only be used in generic constraints}} +} +