Skip to content

Don't emit marker protocols into runtime type metadata. #39812

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,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 @@ -293,6 +296,9 @@ class ASTMangler : public Mangler {
static const clang::NamedDecl *
getClangDeclForMangling(const ValueDecl *decl);

void appendExistentialLayout(
const ExistentialLayout &layout, 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 @@ -5827,6 +5827,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
71 changes: 52 additions & 19 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,40 @@ void ASTMangler::appendOpaqueDeclName(const OpaqueTypeDecl *opaqueDecl) {
}
}

void ASTMangler::appendExistentialLayout(
const ExistentialLayout &layout, 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, 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, const ValueDecl *forDecl) {
Expand Down Expand Up @@ -1183,31 +1217,15 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) {
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)), 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, forDecl);
return appendOperator("Xc");
} else if (layout.hasExplicitAnyObject) {
return appendOperator("Xl");
}
return appendOperator("p");
return appendExistentialLayout(layout, forDecl);
}

case TypeKind::UnboundGeneric:
Expand Down Expand Up @@ -2190,6 +2208,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 @@ -2338,6 +2358,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 @@ -2651,6 +2673,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 @@ -3181,6 +3208,12 @@ void ASTMangler::appendAnyProtocolConformance(
CanGenericSignature 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, [&]{
bindGenericParameters(Sig);
appendType(Ty);
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5588,6 +5588,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