Skip to content

Sema: Allow protocols to inherit from subclass existentials [5.1] #24141

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
54 changes: 31 additions & 23 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ static void checkInheritanceClause(
inheritedClause = typeDecl->getInherited();
}

// Can this declaration's inheritance clause contain a class or
// subclass existential?
bool canHaveSuperclass = (isa<ClassDecl>(decl) ||
(isa<ProtocolDecl>(decl) &&
!cast<ProtocolDecl>(decl)->isObjC()));
Expand Down Expand Up @@ -351,34 +353,38 @@ static void checkInheritanceClause(
if (inheritedTy->isExistentialType()) {
auto layout = inheritedTy->getExistentialLayout();

// Inheritance from protocol compositions that do not contain classes
// or AnyObject is always OK.
if (!layout.hasExplicitAnyObject &&
!layout.explicitSuperclass)
// Subclass existentials are not allowed except on classes and
// non-@objc protocols.
if (layout.explicitSuperclass &&
!canHaveSuperclass) {
decl->diagnose(diag::inheritance_from_protocol_with_superclass,
inheritedTy);
continue;
}

// Protocols can inherit from AnyObject.
// AnyObject is not allowed except on protocols.
if (layout.hasExplicitAnyObject &&
isa<ProtocolDecl>(decl))
!isa<ProtocolDecl>(decl)) {
decl->diagnose(canHaveSuperclass
? diag::inheritance_from_non_protocol_or_class
: diag::inheritance_from_non_protocol,
inheritedTy);
continue;
}

// Class-constrained protocol compositions are not allowed except in
// special cases.
if (layout.explicitSuperclass) {
if (!canHaveSuperclass) {
decl->diagnose(diag::inheritance_from_protocol_with_superclass,
inheritedTy);
continue;
}
// If the existential did not have a class constraint, we're done.
if (!layout.explicitSuperclass)
continue;

// Classes can inherit from protocol compositions that contain a
// superclass, but not AnyObject.
if (isa<ClassDecl>(decl) &&
!layout.hasExplicitAnyObject) {
// Superclass inheritance is handled below.
inheritedTy = layout.explicitSuperclass;
}
}
// Classes and protocols can inherit from subclass existentials.
// For classes, we check for a duplicate superclass below.
// For protocols, the GSB emits its own warning instead.
if (isa<ProtocolDecl>(decl))
continue;

assert(isa<ClassDecl>(decl));
assert(canHaveSuperclass);
inheritedTy = layout.explicitSuperclass;
}

// If this is an enum inheritance clause, check for a raw type.
Expand Down Expand Up @@ -418,7 +424,9 @@ static void checkInheritanceClause(
continue;
}

// If this is a class type, it may be the superclass.
// If this is a class type, it may be the superclass. We end up here when
// the inherited type is either itself a class, or when it is a subclass
// existential via the existential type path above.
if (inheritedTy->getClassOrBoundGenericClass()) {
// First, check if we already had a superclass.
if (superclassTy) {
Expand Down
12 changes: 12 additions & 0 deletions test/decl/protocol/protocol_with_superclass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,15 @@ extension Amb { // expected-error {{'Amb' is ambiguous for type lookup in this c
_ = ConcreteAlias.self
}
}

protocol ProtoRefinesClassComposition : Generic<Int> & BaseProto {}

func usesProtoRefinesClass1(_ t: ProtoRefinesClassComposition) {
t.genericMethod((1, 2))
let _: BaseProto = t
}

func usesProtoRefinesClass2<T : ProtoRefinesClassComposition>(_ t: T) {
t.genericMethod((1, 2))
let _: BaseProto = t
}
3 changes: 3 additions & 0 deletions test/decl/protocol/protocol_with_superclass_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ typealias Composition = OtherProtocol & Base

@objc protocol Protocol2 : Composition {}
// expected-error@-1 {{inheritance from class-constrained protocol composition type 'Composition' (aka 'Base & OtherProtocol')}}

@objc protocol Protocol3 : OtherProtocol & Base {}
// expected-error@-1 {{inheritance from class-constrained protocol composition type 'Base & OtherProtocol'}}