Skip to content

Preliminary SILGen support for subclass existentials (SE-0156) #8685

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
Merged
1 change: 1 addition & 0 deletions docs/ABI.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,7 @@ Types
type ::= associated-type
type ::= nominal-type
type ::= protocol-list 'p' // existential type
type ::= protocol-list superclass 'XE' // existential type with superclass
type ::= type-list 't' // tuple
type ::= type generic-signature 'u' // generic type
type ::= 'x' // generic param, depth=0, idx=0
Expand Down
6 changes: 5 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3093,7 +3093,11 @@ ERROR(function_type_no_parens,none,
NOTE(not_objc_empty_protocol_composition,none,
"'Any' is not considered '@objc'; use 'AnyObject' instead", ())
NOTE(not_objc_protocol,none,
"protocol %0 is not '@objc'", (Type))
"protocol-constrained type containing protocol %0 cannot be represented "
"in Objective-C", (Type))
NOTE(not_objc_class_constraint,none,
"protocol-constrained type containing class %0 cannot be represented "
"in Objective-C", (Type))
NOTE(not_objc_error_protocol_composition,none,
"protocol-constrained type containing 'Error' cannot be represented "
"in Objective-C", ())
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/ExistentialLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,13 @@ struct ExistentialLayout {
return requiresClass && !containsNonObjCProtocol;
}

// Does this existential contain the Error protocol?
bool isExistentialWithError(ASTContext &ctx) const;

// Does this existential consist of an Error protocol only with no other
// constraints?
bool isErrorExistential() const;

ArrayRef<ProtocolType *> getProtocols() const {
if (singleProtocol)
return ArrayRef<ProtocolType *>{&singleProtocol, 1};
Expand Down
15 changes: 0 additions & 15 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -4552,21 +4552,6 @@ inline CanType CanType::getNominalParent() const {
}
}

inline bool TypeBase::mayHaveSuperclass() {
if (getClassOrBoundGenericClass())
return true;

// FIXME: requiresClass() is not the same as having an explicit superclass;
// is this wrong?
if (auto archetype = getAs<ArchetypeType>())
return (bool)archetype->requiresClass();

if (isExistentialType())
return (bool)getSuperclass(nullptr);

return is<DynamicSelfType>();
}

inline TupleTypeElt::TupleTypeElt(Type ty, Identifier name, bool isVariadic,
bool isAutoClosure, bool isEscaping)
: Name(name), ElementType(ty),
Expand Down
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ CONTEXT_NODE(Protocol)
NODE(ProtocolConformance)
NODE(ProtocolDescriptor)
NODE(ProtocolList)
NODE(ProtocolListWithClass)
NODE(ProtocolWitness)
NODE(ProtocolWitnessTable)
NODE(ProtocolWitnessTableAccessor)
Expand Down
21 changes: 19 additions & 2 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/AST/ASTMangler.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/Module.h"
Expand Down Expand Up @@ -631,16 +632,32 @@ void ASTMangler::appendType(Type type) {
appendTypeList(type);
return appendOperator("t");

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

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

if (layout.superclass) {
appendType(layout.superclass);
return appendOperator("XE");
}
return appendOperator("p");
}

case TypeKind::UnboundGeneric:
case TypeKind::Class:
case TypeKind::Enum:
Expand Down
35 changes: 31 additions & 4 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ ExistentialLayout::ExistentialLayout(ProtocolCompositionType *type) {
isa<ClassDecl>(members[0]->getAnyNominal())) {
superclass = members[0];
members = members.slice(1);
requiresClass = true;
requiresClassImplied = true;
}

for (auto member : members) {
Expand Down Expand Up @@ -600,6 +602,13 @@ bool TypeBase::isAnyObject() {
return canTy.getExistentialLayout().isAnyObject();
}

bool ExistentialLayout::isErrorExistential() const {
auto protocols = getProtocols();
return (!requiresClass &&
protocols.size() == 1 &&
protocols[0]->getDecl()->isSpecificProtocol(KnownProtocolKind::Error));
}

bool ExistentialLayout::isExistentialWithError(ASTContext &ctx) const {
auto errorProto = ctx.getProtocol(KnownProtocolKind::Error);
if (!errorProto) return false;
Expand Down Expand Up @@ -1562,6 +1571,16 @@ LayoutConstraint TypeBase::getLayoutConstraint() {
return LayoutConstraint();
}

bool TypeBase::mayHaveSuperclass() {
if (getClassOrBoundGenericClass())
return true;

if (auto archetype = getAs<ArchetypeType>())
return (bool)archetype->requiresClass();

return is<DynamicSelfType>();
}

Type TypeBase::getSuperclass(LazyResolver *resolver) {
auto *nominalDecl = getAnyNominal();
auto *classDecl = dyn_cast_or_null<ClassDecl>(nominalDecl);
Expand Down Expand Up @@ -1987,10 +2006,18 @@ getObjCObjectRepresentable(Type type, const DeclContext *dc) {
return ForeignRepresentableKind::Object;
}

// Objective-C existential types.
if (type->isObjCExistentialType())
return ForeignRepresentableKind::Object;

// Objective-C existential types are trivially representable if
// they don't have a superclass constraint, or if the superclass
// constraint is an @objc class.
if (type->isExistentialType()) {
auto layout = type->getExistentialLayout();
if (layout.isObjC() &&
(!layout.superclass ||
getObjCObjectRepresentable(layout.superclass, dc) ==
ForeignRepresentableKind::Object))
return ForeignRepresentableKind::Object;
}

// Any can be bridged to id.
if (type->isAny()) {
return ForeignRepresentableKind::Bridged;
Expand Down
6 changes: 6 additions & 0 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,12 @@ NodePointer Demangler::demangleSpecialType() {
case 'p':
return createType(createWithChild(Node::Kind::ExistentialMetatype,
popNode(Node::Kind::Type)));
case 'E': {
NodePointer Superclass = popNode(Node::Kind::Type);
NodePointer Protocols = demangleProtocolListType();
return createType(createWithChildren(Node::Kind::ProtocolListWithClass,
Protocols, Superclass));
}
case 'X':
case 'x': {
// SIL box types.
Expand Down
14 changes: 13 additions & 1 deletion lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ class NodePrinter {
return true;
return false;

case Node::Kind::ProtocolListWithClass:
case Node::Kind::Allocator:
case Node::Kind::ArgumentTuple:
case Node::Kind::AssociatedTypeMetadataAccessor:
Expand Down Expand Up @@ -578,7 +579,8 @@ class NodePrinter {

static bool isExistentialType(NodePointer node) {
return (node->getKind() == Node::Kind::ExistentialMetatype ||
node->getKind() == Node::Kind::ProtocolList);
node->getKind() == Node::Kind::ProtocolList ||
node->getKind() == Node::Kind::ProtocolListWithClass);
}

/// Print the relevant parameters and return the new index.
Expand Down Expand Up @@ -1388,6 +1390,16 @@ void NodePrinter::print(NodePointer pointer, bool asContext, bool suppressType)
printChildren(type_list, " & ");
return;
}
case Node::Kind::ProtocolListWithClass: {
if (pointer->getNumChildren() < 2)
return;
NodePointer protocols = pointer->getChild(0);
NodePointer superclass = pointer->getChild(1);
print(superclass);
Printer << " & ";
printChildren(protocols, " & ");
return;
}
case Node::Kind::AssociatedType:
// Don't print for now.
return;
Expand Down
20 changes: 18 additions & 2 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ class Remangler {
mangleChildNodes(Proto);
}

void mangleProtocolList(Node *protocols, Node *superclass);

bool trySubstitution(Node *node, SubstitutionEntry &entry,
bool treatAsIdentifier = false);
void addSubstitution(const SubstitutionEntry &entry);
Expand Down Expand Up @@ -1401,17 +1403,31 @@ void Remangler::mangleProtocolDescriptor(Node *node) {
Buffer << "Mp";
}

void Remangler::mangleProtocolList(Node *node) {
node = getSingleChild(node, Node::Kind::TypeList);
void Remangler::mangleProtocolList(Node *node, Node *superclass) {
bool FirstElem = true;
for (NodePointer Child : *node) {
manglePureProtocol(Child);
mangleListSeparator(FirstElem);
}
mangleEndOfList(FirstElem);
if (superclass) {
mangleType(superclass);
Buffer << "XE";
return;
}
Buffer << 'p';
}

void Remangler::mangleProtocolList(Node *node) {
auto *protocols = getSingleChild(node, Node::Kind::TypeList);
mangleProtocolList(protocols, nullptr);
}

void Remangler::mangleProtocolListWithClass(Node *node) {
mangleProtocolList(node->getChild(0),
node->getChild(1));
}

void Remangler::mangleProtocolWitness(Node *node) {
mangleChildNodes(node);
Buffer << "TW";
Expand Down
35 changes: 12 additions & 23 deletions lib/SIL/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "swift/SIL/SILType.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Type.h"
#include "swift/SIL/SILModule.h"
Expand Down Expand Up @@ -473,16 +474,9 @@ static bool isBridgedErrorClass(SILModule &M,
return false;
}

static bool isErrorExistential(ArrayRef<ProtocolDecl*> protocols) {
return protocols.size() == 1
&& protocols[0]->isSpecificProtocol(KnownProtocolKind::Error);
}

ExistentialRepresentation
SILType::getPreferredExistentialRepresentation(SILModule &M,
Type containedType) const {
SmallVector<ProtocolDecl *, 4> protocols;

// Existential metatypes always use metatype representation.
if (is<ExistentialMetatypeType>())
return ExistentialRepresentation::Metatype;
Expand All @@ -491,11 +485,9 @@ SILType::getPreferredExistentialRepresentation(SILModule &M,
if (!isExistentialType())
return ExistentialRepresentation::None;

// Get the list of existential constraints.
getSwiftRValueType()->getExistentialTypeProtocols(protocols);
auto layout = getSwiftRValueType().getExistentialLayout();

// The (uncomposed) Error existential uses a special boxed representation.
if (isErrorExistential(protocols)) {
if (layout.isErrorExistential()) {
// NSError or CFError references can be adopted directly as Error
// existentials.
if (isBridgedErrorClass(M, containedType)) {
Expand All @@ -507,10 +499,8 @@ SILType::getPreferredExistentialRepresentation(SILModule &M,

// A class-constrained protocol composition can adopt the conforming
// class reference directly.
for (auto proto : protocols) {
if (proto->requiresClass())
return ExistentialRepresentation::Class;
}
if (layout.requiresClass)
return ExistentialRepresentation::Class;

// Otherwise, we need to use a fixed-sized buffer.
return ExistentialRepresentation::Opaque;
Expand All @@ -529,23 +519,22 @@ SILType::canUseExistentialRepresentation(SILModule &M,
// Look at the protocols to see what representation is appropriate.
if (!getSwiftRValueType().isExistentialType())
return false;
SmallVector<ProtocolDecl *, 4> protocols;
getSwiftRValueType().getExistentialTypeProtocols(protocols);

auto layout = getSwiftRValueType().getExistentialLayout();

// The (uncomposed) Error existential uses a special boxed
// representation. It can also adopt class references of bridged error types
// directly.
if (isErrorExistential(protocols))
if (layout.isErrorExistential())
return repr == ExistentialRepresentation::Boxed
|| (repr == ExistentialRepresentation::Class
&& isBridgedErrorClass(M, containedType));

// A class-constrained composition uses ClassReference representation;
// otherwise, we use a fixed-sized buffer
for (auto *proto : protocols) {
if (proto->requiresClass())
return repr == ExistentialRepresentation::Class;
}
// otherwise, we use a fixed-sized buffer.
if (layout.requiresClass)
return repr == ExistentialRepresentation::Class;

return repr == ExistentialRepresentation::Opaque;
}
case ExistentialRepresentation::Metatype:
Expand Down
Loading