Skip to content

Commit

Permalink
Don't emit marker protocols into runtime type metadata.
Browse files Browse the repository at this point in the history
Marker protocols don't exist at runtime, drop them when mangling a type
for the purposes of runtime type metadata or reflection. Fixes
rdar://82314404.
  • Loading branch information
DougGregor committed Oct 16, 2021
1 parent 618f0de commit 7d7ec9e
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 20 deletions.
7 changes: 7 additions & 0 deletions include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class ASTMangler : public Mangler {
/// concurrency library.
bool AllowConcurrencyStandardSubstitutions = true;

/// If enabled, marker protocols can be encoded in the mangled name.
bool AllowMarkerProtocols = true;

public:
using SymbolicReferent = llvm::PointerUnion<const NominalTypeDecl *,
const OpaqueTypeDecl *>;
Expand Down Expand Up @@ -294,6 +297,10 @@ class ASTMangler : public Mangler {
static const clang::NamedDecl *
getClangDeclForMangling(const ValueDecl *decl);

void appendExistentialLayout(
const ExistentialLayout &layout, GenericSignature sig,
const ValueDecl *forDecl);

protected:

void appendSymbolKind(SymbolKind SKind);
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5943,6 +5943,9 @@ ERROR(marker_protocol_requirement, none,
ERROR(marker_protocol_inherit_nonmarker, none,
"marker protocol %0 cannot inherit non-marker protocol %1",
(DeclName, DeclName))
ERROR(marker_protocol_inherit_class, none,
"marker protocol %0 cannot inherit class %1",
(DeclName, Type))
ERROR(marker_protocol_cast,none,
"marker protocol %0 cannot be used in a conditional cast", (DeclName))
ERROR(marker_protocol_conditional_conformance,none,
Expand Down
72 changes: 53 additions & 19 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,41 @@ void ASTMangler::appendOpaqueDeclName(const OpaqueTypeDecl *opaqueDecl) {
}
}

void ASTMangler::appendExistentialLayout(
const ExistentialLayout &layout, GenericSignature sig,
const ValueDecl *forDecl) {
bool First = true;
bool DroppedRequiresClass = false;
bool SawRequiresClass = false;
for (Type protoTy : layout.getProtocols()) {
auto proto = protoTy->castTo<ProtocolType>()->getDecl();
// If we aren't allowed to emit marker protocols, suppress them here.
if (!AllowMarkerProtocols && proto->isMarkerProtocol()) {
if (proto->requiresClass())
DroppedRequiresClass = true;

continue;
}

if (proto->requiresClass())
SawRequiresClass = true;

appendProtocolName(protoTy->castTo<ProtocolType>()->getDecl());
appendListSeparator(First);
}
if (First)
appendOperator("y");

if (auto superclass = layout.explicitSuperclass) {
appendType(superclass, sig, forDecl);
return appendOperator("Xc");
} else if (layout.hasExplicitAnyObject ||
(DroppedRequiresClass && !SawRequiresClass)) {
return appendOperator("Xl");
}
return appendOperator("p");
}

/// Mangle a type into the buffer.
///
void ASTMangler::appendType(Type type, GenericSignature sig,
Expand Down Expand Up @@ -1199,31 +1234,15 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
return appendOperator("t");

case TypeKind::Protocol: {
bool First = true;
appendProtocolName(cast<ProtocolType>(tybase)->getDecl());
appendListSeparator(First);
return appendOperator("p");
return appendExistentialLayout(
ExistentialLayout(cast<ProtocolType>(tybase)), sig, forDecl);
}

case TypeKind::ProtocolComposition: {
// We mangle ProtocolType and ProtocolCompositionType using the
// same production:
bool First = true;
auto layout = type->getExistentialLayout();
for (Type protoTy : layout.getProtocols()) {
appendProtocolName(protoTy->castTo<ProtocolType>()->getDecl());
appendListSeparator(First);
}
if (First)
appendOperator("y");

if (auto superclass = layout.explicitSuperclass) {
appendType(superclass, sig, forDecl);
return appendOperator("Xc");
} else if (layout.hasExplicitAnyObject) {
return appendOperator("Xl");
}
return appendOperator("p");
return appendExistentialLayout(layout, sig, forDecl);
}

case TypeKind::UnboundGeneric:
Expand Down Expand Up @@ -2220,6 +2239,8 @@ void ASTMangler::appendModule(const ModuleDecl *module,
/// Mangle the name of a protocol as a substitution candidate.
void ASTMangler::appendProtocolName(const ProtocolDecl *protocol,
bool allowStandardSubstitution) {
assert(AllowMarkerProtocols || !protocol->isMarkerProtocol());

if (allowStandardSubstitution && tryAppendStandardSubstitution(protocol))
return;

Expand Down Expand Up @@ -2370,6 +2391,8 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) {
appendOperator("a");
break;
case DeclKind::Protocol:
assert(AllowMarkerProtocols ||
!cast<ProtocolDecl>(decl)->isMarkerProtocol());
appendOperator("P");
break;
case DeclKind::Class:
Expand Down Expand Up @@ -2689,6 +2712,11 @@ void ASTMangler::appendRequirement(const Requirement &reqt,
case RequirementKind::Layout: {
} break;
case RequirementKind::Conformance: {
// If we don't allow marker protocols but we have one here, skip it.
if (!AllowMarkerProtocols &&
reqt.getProtocolDecl()->isMarkerProtocol())
return;

appendProtocolName(reqt.getProtocolDecl());
} break;
case RequirementKind::Superclass:
Expand Down Expand Up @@ -3226,6 +3254,12 @@ void ASTMangler::appendAnyProtocolConformance(
GenericSignature genericSig,
CanType conformingType,
ProtocolConformanceRef conformance) {
// If we have a conformance to a marker protocol but we aren't allowed to
// emit marker protocols, skip it.
if (!AllowMarkerProtocols &&
conformance.getRequirement()->isMarkerProtocol())
return;

if (conformingType->isTypeParameter()) {
assert(genericSig && "Need a generic signature to resolve conformance");
auto path = genericSig->getConformanceAccessPath(conformingType,
Expand Down
2 changes: 2 additions & 0 deletions lib/IRGen/IRGenMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ IRGenMangler::mangleTypeForReflection(IRGenModule &IGM,
AllowConcurrencyStandardSubstitutions = false;
}

llvm::SaveAndRestore<bool> savedAllowMarkerProtocols(
AllowMarkerProtocols, false);
return withSymbolicReferences(IGM, [&]{
appendType(Ty, Sig);
});
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5574,6 +5574,12 @@ void AttributeChecker::visitMarkerAttr(MarkerAttr *attr) {
}
}

if (Type superclass = proto->getSuperclass()) {
proto->diagnose(
diag::marker_protocol_inherit_class,
proto->getName(), superclass);
}

// A marker protocol cannot have any requirements.
for (auto member : proto->getAllMembers()) {
auto value = dyn_cast<ValueDecl>(member);
Expand Down
31 changes: 30 additions & 1 deletion test/IRGen/marker_protocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
extension Int: P { }
extension Array: P where Element: P { }

// CHECK: @"$s15marker_protocol1QMp" = {{(dllexport |protected )?}}constant
// No mention of the marker protocol for runtime type instantiation.
// CHECK-LABEL: @"$sSS_15marker_protocol1P_ptMD" =
// CHECK-SAME: @"symbolic SS_ypt"

// CHECK-LABEL: @"$s15marker_protocol1QMp" = {{(dllexport |protected )?}}constant
// CHECK-SAME: i32 trunc{{.*}}s15marker_protocolMXM{{.*}}s15marker_protocol1QMp
// CHECK-SAME: i32 0, i32 5, i32 0
public protocol Q: P {
Expand All @@ -24,6 +28,31 @@ public protocol Q: P {
func j()
}

protocol R { }

@_marker protocol S: AnyObject { }

// Note: no mention of marker protocols here.
// CHECK-LABEL: @"$s15marker_protocol10HasMarkersVMF" =
// CHECK-SAME: @"symbolic yp"
// CHECK-SAME: @"symbolic ______p 15marker_protocol1QP"
// CHECK-SAME: @"symbolic ______p 15marker_protocol1RP"
// CHECK-SAME: @"symbolic yXl"
struct HasMarkers {
var field1: P
var field2: P & Q
var field3: P & R
var field4: S
}

// Note: no mention of marker protocols when forming a dictionary.
// CHECK-LABEL: define{{.*}}@"$s15marker_protocol0A12InDictionaryypyF"
// CHECK: call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSS_15marker_protocol1P_ptMD")
public func markerInDictionary() -> Any {
let dict: [String: P] = ["answer" : 42]
return dict
}

// Note: no witness tables
// CHECK: swiftcc void @"$s15marker_protocol7genericyyxAA1PRzlF"(%swift.opaque* noalias nocapture %0, %swift.type* %T)
public func generic<T: P>(_: T) { }
Expand Down
4 changes: 4 additions & 0 deletions test/attr/attr_marker_protocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ protocol P4 { } // expected-note{{'P4' declared here}}

@_marker protocol P5: P4 { } // expected-error{{marker protocol 'P5' cannot inherit non-marker protocol 'P4'}}

class C { }
@_marker protocol P5a: AnyObject { } // okay
@_marker protocol P5b: C { } // expected-error{{marker protocol 'P5b' cannot inherit class 'C'}}

// Legitimate uses of marker protocols.
extension P3 {
func f() { }
Expand Down

0 comments on commit 7d7ec9e

Please sign in to comment.