Skip to content

Protocol superclass constraints part 4 #17851

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 6 commits into from
Jul 10, 2018
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/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,10 @@ WARNING(protocol_composition_with_postfix,none,
"protocol-constrained type with postfix '%0' is ambiguous "
"and will be rejected in future version of Swift", (StringRef))

ERROR(protocol_where_clause_self_requirement,none,
"constraint with subject type of 'Self' is not supported; "
"consider adding requirement to protocol inheritance clause instead", ())

ERROR(invalid_protocol_composition_member,none,
"non-protocol, non-class type %0 cannot be used within a "
"protocol-constrained type", (Type))
Expand Down Expand Up @@ -2128,6 +2132,8 @@ ERROR(duplicate_inheritance,none,
"duplicate inheritance from %0", (Type))
WARNING(duplicate_anyobject_class_inheritance,none,
"redundant inheritance from 'AnyObject' and Swift 3 'class' keyword", ())
ERROR(objc_protocol_with_superclass,none,
"protocol %0 is '@objc' and cannot have a superclass constraint", (Identifier))
ERROR(multiple_inheritance,none,
"multiple inheritance from classes %0 and %1", (Type, Type))
ERROR(non_class_inheritance,none,
Expand Down
4 changes: 2 additions & 2 deletions lib/IRGen/ClassMetadataVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ template <class Impl> class ClassMetadataVisitor
// isn't necessary.
// FIXME: Figure out what can be removed altogether in non-objc-interop
// mode and remove it. rdar://problem/18801263
asImpl().addSuperClass();
asImpl().addSuperclass();
asImpl().addClassCacheData();
asImpl().addClassDataPointer();

Expand Down Expand Up @@ -171,7 +171,7 @@ class ClassMetadataScanner : public ClassMetadataVisitor<Impl> {
void addIVarDestroyer() { addPointer(); }
void addValueWitnessTable() { addPointer(); }
void addDestructorFunction() { addPointer(); }
void addSuperClass() { addPointer(); }
void addSuperclass() { addPointer(); }
void addClassFlags() { addInt32(); }
void addInstanceAddressPoint() { addInt32(); }
void addInstanceSize() { addInt32(); }
Expand Down
4 changes: 2 additions & 2 deletions lib/IRGen/ForeignClassMetadataVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ForeignClassMetadataVisitor
super::layout();
asImpl().addNominalTypeDescriptor();
asImpl().noteStartOfSuperClass();
asImpl().addSuperClass();
asImpl().addSuperclass();
asImpl().addReservedWord();
asImpl().addReservedWord();
asImpl().addReservedWord();
Expand Down Expand Up @@ -70,7 +70,7 @@ class ForeignClassMetadataScanner : public ForeignClassMetadataVisitor<Impl> {
void addMetadataFlags() { addPointer(); }
void addValueWitnessTable() { addPointer(); }
void addNominalTypeDescriptor() { addPointer(); }
void addSuperClass() { addPointer(); }
void addSuperclass() { addPointer(); }
void addReservedWord() { addPointer(); }

private:
Expand Down
18 changes: 13 additions & 5 deletions lib/IRGen/GenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
bool hasClassConstraint = layout.requiresClass();
bool hasClassConstraintByProtocol = false;

bool hasSuperclassConstraint = bool(layout.getSuperclass());
bool hasSuperclassConstraint = bool(layout.explicitSuperclass);

for (auto protoTy : layout.getProtocols()) {
auto *protoDecl = protoTy->getDecl();
Expand Down Expand Up @@ -581,9 +581,17 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
// Note that destInstanceType is always an existential type, so calling
// getSuperclass() returns the superclass constraint of the existential,
// not the superclass of some concrete class.
bool checkSuperclassConstraint =
hasSuperclassConstraint &&
!destInstanceType->getSuperclass()->isExactSuperclassOf(srcInstanceType);
bool checkSuperclassConstraint = false;
if (hasSuperclassConstraint) {
Type srcSuperclassType = srcInstanceType;
if (srcSuperclassType->isExistentialType())
srcSuperclassType = srcSuperclassType->getSuperclass();
if (srcSuperclassType) {
checkSuperclassConstraint =
!destInstanceType->getSuperclass()->isExactSuperclassOf(
srcSuperclassType);
}
}

if (checkSuperclassConstraint)
checkClassConstraint = true;
Expand Down Expand Up @@ -739,7 +747,7 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
args.push_back(metadataValue);

if (checkSuperclassConstraint)
args.push_back(IGF.emitTypeMetadataRef(CanType(layout.getSuperclass())));
args.push_back(IGF.emitTypeMetadataRef(CanType(layout.explicitSuperclass)));

for (auto proto : witnessTableProtos)
args.push_back(proto);
Expand Down
4 changes: 2 additions & 2 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2041,7 +2041,7 @@ namespace {
AddressPoint = B.getNextOffsetFromGlobal();
}

void addSuperClass() {
void addSuperclass() {
// If this is a root class, use SwiftObject as our formal parent.
if (!Target->hasSuperclass()) {
// This is only required for ObjC interoperation.
Expand Down Expand Up @@ -3246,7 +3246,7 @@ namespace {

void noteStartOfSuperClass() { }

void addSuperClass() {
void addSuperclass() {
auto superclassDecl = Target->getSuperclassDecl();
if (!superclassDecl || !superclassDecl->isForeign()) {
B.addNullPointer(IGM.TypeMetadataPtrTy);
Expand Down
34 changes: 27 additions & 7 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ void TypeChecker::validateWhereClauses(ProtocolDecl *protocol,
GenericTypeResolver *resolver) {
TypeResolutionOptions options;

options |= TypeResolutionFlags::ProtocolWhereClause;

if (auto whereClause = protocol->getTrailingWhereClause()) {
revertGenericRequirements(whereClause->getRequirements());
validateRequirements(whereClause->getWhereLoc(),
Expand Down Expand Up @@ -288,9 +290,7 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
inheritedClause = type->getInherited();
} else {
auto ext = cast<ExtensionDecl>(decl);

validateExtension(ext);
if (ext->isInvalid())
if (!ext->getExtendedType())
return;

inheritedClause = ext->getInherited();
Expand Down Expand Up @@ -408,17 +408,27 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
}
inheritedTypes[inheritedCanTy] = { i, inherited.getSourceRange() };

// If this is a protocol or protocol composition type, record the
// protocols.
if (inheritedTy->isExistentialType()) {
auto layout = inheritedTy->getExistentialLayout();

// @objc protocols cannot have superclass constraints.
if (layout.explicitSuperclass) {
if (auto *protoDecl = dyn_cast<ProtocolDecl>(decl)) {
if (protoDecl->isObjC()) {
diagnose(protoDecl,
diag::objc_protocol_with_superclass,
protoDecl->getName());
continue;
}
}
}

// Protocols, generic parameters and associated types can inherit
// from subclass existentials, which are "exploded" into their
// corresponding requirements.
//
// Classes can only inherit from subclass existentials that do not
// have a superclass term.
// Extensions, structs and enums can only inherit from protocol
// compositions that do not contain AnyObject or class members.
if (isa<ProtocolDecl>(decl) ||
isa<AbstractTypeParamDecl>(decl) ||
(!layout.hasExplicitAnyObject &&
Expand Down Expand Up @@ -494,6 +504,16 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
continue;
}

// @objc protocols cannot have superclass constraints.
if (auto *protoDecl = dyn_cast<ProtocolDecl>(decl)) {
if (protoDecl->isObjC()) {
diagnose(protoDecl,
diag::objc_protocol_with_superclass,
protoDecl->getName());
continue;
}
}

// If the declaration we're looking at doesn't allow a superclass,
// complain.
if (isa<StructDecl>(decl) || isa<ExtensionDecl>(decl)) {
Expand Down
25 changes: 25 additions & 0 deletions lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,31 @@ bool TypeChecker::validateRequirement(SourceLoc whereLoc, RequirementRepr &req,
// Note that we are resolving within a requirement.
options |= TypeResolutionFlags::GenericRequirement;

// Protocol where clauses cannot add conformance and superclass constraints
// to 'Self', because we need to be able to resolve inherited protocols and
// protocol superclasses before computing the protocol requirement signature.
if (options & TypeResolutionFlags::ProtocolWhereClause) {
if (req.getKind() == RequirementReprKind::TypeConstraint ||
req.getKind() == RequirementReprKind::LayoutConstraint) {
if (auto *subjectTyR = req.getSubjectLoc().getTypeRepr()) {
if (auto *componentTyR = dyn_cast<ComponentIdentTypeRepr>(subjectTyR)) {
if (componentTyR->getIdentifier() == Context.Id_Self) {
diagnose(req.getSubjectLoc().getLoc(),
diag::protocol_where_clause_self_requirement);

req.getSubjectLoc().setType(ErrorType::get(Context));

if (req.getKind() == RequirementReprKind::TypeConstraint)
req.getConstraintLoc().setType(ErrorType::get(Context));

req.setInvalid();
return true;
}
}
}
}
}

switch (req.getKind()) {
case RequirementReprKind::TypeConstraint: {
// Validate the types.
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,9 @@ enum class TypeResolutionFlags : unsigned {

/// Whether we are in a requirement of a generic declaration
GenericRequirement = 1 << 26,

/// Whether we are in a protocol's where clause
ProtocolWhereClause = 1 << 27,
};

/// Option set describing how type resolution should work.
Expand Down
8 changes: 4 additions & 4 deletions test/IDE/print_ast_tc_decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1352,11 +1352,11 @@ extension ProtocolToExtend where Self.Assoc == Int {}

// Protocol with where clauses

protocol ProtocolWithWhereClause : QuxProtocol where Qux == Int, Self : FooProtocol {}
// PREFER_TYPE_REPR_PRINTING: protocol ProtocolWithWhereClause : FooProtocol, QuxProtocol where Self.Qux == Int {
protocol ProtocolWithWhereClause : QuxProtocol where Qux == Int {}
// PREFER_TYPE_REPR_PRINTING: protocol ProtocolWithWhereClause : QuxProtocol where Self.Qux == Int {

protocol ProtocolWithWhereClauseAndAssoc : QuxProtocol where Qux == Int, Self : FooProtocol {
// PREFER_TYPE_REPR_PRINTING-DAG: protocol ProtocolWithWhereClauseAndAssoc : FooProtocol, QuxProtocol where Self.Qux == Int {
protocol ProtocolWithWhereClauseAndAssoc : QuxProtocol where Qux == Int {
// PREFER_TYPE_REPR_PRINTING-DAG: protocol ProtocolWithWhereClauseAndAssoc : QuxProtocol where Self.Qux == Int {
associatedtype A1 : QuxProtocol where A1 : FooProtocol, A1.Qux : QuxProtocol, Int == A1.Qux.Qux
// PREFER_TYPE_REPR_PRINTING-DAG: {{^}} associatedtype A1 : FooProtocol, QuxProtocol where Self.A1.Qux : QuxProtocol, Self.A1.Qux.Qux == Int{{$}}

Expand Down
Loading