[swift-dev] Patch RFC: value-type bound protocols
David Zarzycki
zarzycki at icloud.com
Wed Sep 13 19:24:44 CDT 2017
I’d still like some feedback from the core team about whether value-type bound protocols are worth formally proposing (and if so, what is the process like these days). Thanks!
Dave
https://github.com/davezarzycki/swift/commit/32f32ce009f649772df57c94e2958bed4ef142ab
Or inline:
diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h
index b484580f46..141993768a 100644
--- a/include/swift/AST/Decl.h
+++ b/include/swift/AST/Decl.h
@@ -470,6 +470,9 @@ class alignas(1 << DeclAlignInBits) Decl {
/// Whether this is a class-bounded protocol.
unsigned RequiresClass : 1;
+ /// Whether this is a value-type-bounded protocol.
+ unsigned RequiresNonClass : 1;
+
/// Whether the \c ExistentialConformsToSelf bit is valid.
unsigned ExistentialConformsToSelfValid : 1;
@@ -485,7 +488,7 @@ class alignas(1 << DeclAlignInBits) Decl {
/// The stage of the circularity check for this protocol.
unsigned Circularity : 2;
};
- enum { NumProtocolDeclBits = NumNominalTypeDeclBits + 8 };
+ enum { NumProtocolDeclBits = NumNominalTypeDeclBits + 9 };
static_assert(NumProtocolDeclBits <= 32, "fits in an unsigned");
class ClassDeclBitfields {
@@ -3495,6 +3498,8 @@ private:
class ProtocolDecl final : public NominalTypeDecl {
SourceLoc ProtocolLoc;
+ SourceLoc NotClassLoc;
+
/// The syntactic representation of the where clause in a protocol like
/// `protocol ... where ... { ... }`.
TrailingWhereClause *TrailingWhere;
@@ -3516,7 +3521,7 @@ class ProtocolDecl final : public NominalTypeDecl {
/// The number of requirements in the requirement signature.
unsigned NumRequirementsInSignature : 16;
- bool requiresClassSlow();
+ bool requiresClassNonClassSlow(bool wantsClass);
bool existentialConformsToSelfSlow();
@@ -3524,7 +3529,8 @@ class ProtocolDecl final : public NominalTypeDecl {
public:
ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc, SourceLoc NameLoc,
- Identifier Name, MutableArrayRef<TypeLoc> Inherited,
+ Identifier Name, SourceLoc NotClassLoc,
+ MutableArrayRef<TypeLoc> Inherited,
TrailingWhereClause *TrailingWhere);
using Decl::getASTContext;
@@ -3564,7 +3570,8 @@ public:
if (ProtocolDeclBits.RequiresClassValid)
return ProtocolDeclBits.RequiresClass;
- return const_cast<ProtocolDecl *>(this)->requiresClassSlow();
+ return const_cast<ProtocolDecl *>(this)->requiresClassNonClassSlow(
+ /*wantsClass*/true);
}
/// Specify that this protocol is class-bounded, e.g., because it was
@@ -3572,6 +3579,24 @@ public:
void setRequiresClass(bool requiresClass = true) {
ProtocolDeclBits.RequiresClassValid = true;
ProtocolDeclBits.RequiresClass = requiresClass;
+ assert(!requiresClass || !ProtocolDeclBits.RequiresNonClass);
+ }
+
+ /// True if this protocol can only be conformed to by value types.
+ bool requiresNonClass() const {
+ if (ProtocolDeclBits.RequiresClassValid)
+ return ProtocolDeclBits.RequiresNonClass;
+
+ return const_cast<ProtocolDecl *>(this)->requiresClassNonClassSlow(
+ /*wantsClass*/false);
+ }
+
+ /// Specify that this protocol is value-type-bounded, e.g., because it was
+ /// annotated with the '!class' keyword.
+ void setRequiresNonClass(bool requiresNonClass = true) {
+ ProtocolDeclBits.RequiresClassValid = true;
+ ProtocolDeclBits.RequiresNonClass = requiresNonClass;
+ assert(!requiresNonClass || !ProtocolDeclBits.RequiresClass);
}
/// Determine whether an existential conforming to this protocol can be
diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def
index 63c3da04b2..ba46826ef5 100644
--- a/include/swift/AST/DiagnosticsParse.def
+++ b/include/swift/AST/DiagnosticsParse.def
@@ -1413,6 +1413,8 @@ ERROR(expected_generics_parameter_name,PointsToFirstBadToken,
"expected an identifier to name generic parameter", ())
ERROR(unexpected_class_constraint,none,
"'class' constraint can only appear on protocol declarations", ())
+ERROR(unexpected_not_class_constraint,none,
+ "'!class' constraint can only appear on protocol declarations", ())
NOTE(suggest_anyobject,none,
"did you mean to write an 'AnyObject' constraint?", ())
ERROR(expected_generics_type_restriction,none,
@@ -1427,6 +1429,10 @@ ERROR(redundant_class_requirement,none,
"redundant 'class' requirement", ())
ERROR(late_class_requirement,none,
"'class' must come first in the requirement list", ())
+ERROR(redundant_not_class_requirement,none,
+ "redundant '!class' requirement", ())
+ERROR(late_not_class_requirement,none,
+ "'!class' must come first in the requirement list", ())
ERROR(where_without_generic_params,none,
"'where' clause cannot be attached to "
"%select{a non-generic|a protocol|an associated type}0 "
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index abbfa538d5..49b841f2a5 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -1396,6 +1396,9 @@ NOTE(types_not_equal_requirement,none,
ERROR(non_class_cannot_conform_to_class_protocol,none,
"non-class type %0 cannot conform to class protocol %1",
(Type, Type))
+ERROR(class_cannot_conform_to_value_type_protocol,none,
+ "class type %0 cannot conform to value type protocol %1",
+ (Type, Type))
ERROR(cf_class_cannot_conform_to_objc_protocol,none,
"Core Foundation class %0 cannot conform to @objc protocol %1 because "
"Core Foundation types are not classes in Objective-C",
@@ -1647,6 +1650,9 @@ ERROR(typealias_outside_of_protocol,none,
"type alias %0 can only be used with a concrete type or "
"generic parameter base", (Identifier))
+ERROR(class_vs_not_class_confict,none,
+ "cannot inherit from both class bound and value-type bound protocols", ())
+
ERROR(circular_protocol_def,none,
"circular protocol inheritance %0", (StringRef))
NOTE(protocol_here,none,
diff --git a/include/swift/AST/ExistentialLayout.h b/include/swift/AST/ExistentialLayout.h
index 4c9eae2fd5..3822641d28 100644
--- a/include/swift/AST/ExistentialLayout.h
+++ b/include/swift/AST/ExistentialLayout.h
@@ -59,6 +59,9 @@ struct ExistentialLayout {
/// '& AnyObject' member or because of a superclass or protocol constraint.
bool requiresClass() const;
+ /// Whether the existential requires a value type.
+ bool requiresNonClass() const;
+
// Does this existential contain the Error protocol?
bool isExistentialWithError(ASTContext &ctx) const;
diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h
index e4176cc6ea..1b1573d421 100644
--- a/include/swift/AST/Types.h
+++ b/include/swift/AST/Types.h
@@ -3802,6 +3802,9 @@ public:
/// True if only classes may conform to the protocol.
bool requiresClass();
+ /// True if only value types may conform to the protocol.
+ bool requiresNonClass();
+
// Implement isa/cast/dyncast/etc.
static bool classof(const TypeBase *T) {
return T->getKind() == TypeKind::Protocol;
@@ -3892,6 +3895,10 @@ public:
/// one of its member protocols being class-constrained.
bool requiresClass();
+ /// True if the composition requires the concrete conforming type to
+ /// be a value type due to a member protocol being value-type-constrained.
+ bool requiresNonClass();
+
/// True if the class requirement is stated directly via '& AnyObject'.
bool hasExplicitAnyObject() {
return ProtocolCompositionTypeBits.HasExplicitAnyObject;
@@ -4130,6 +4137,11 @@ public:
/// a superclass constraint.
bool requiresClass() const;
+ /// requiresNonClass - True if the type can only be substituted with value
+ /// types. This is true if the type conforms to one or more value-type
+ /// protocols.
+ bool requiresNonClass() const;
+
/// \brief Retrieve the superclass of this type, if such a requirement exists.
Type getSuperclass() const {
if (!ArchetypeTypeBits.HasSuperclass) return Type();
diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h
index 06d6541ebc..424cfe9dcd 100644
--- a/include/swift/Parse/Parser.h
+++ b/include/swift/Parse/Parser.h
@@ -797,7 +797,8 @@ public:
DeclAttributes &Attributes);
ParserStatus parseInheritance(SmallVectorImpl<TypeLoc> &Inherited,
bool allowClassRequirement,
- bool allowAnyObject);
+ bool allowAnyObject,
+ SourceLoc *notClassLoc);
ParserStatus parseDeclItem(bool &PreviousHadSemi,
Parser::ParseDeclOptions Options,
llvm::function_ref<void(Decl*)> handler);
diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h
index 875b8be5e0..d17656e63e 100644
--- a/include/swift/Serialization/ModuleFormat.h
+++ b/include/swift/Serialization/ModuleFormat.h
@@ -864,6 +864,7 @@ namespace decls_block {
DeclContextIDField, // context decl
BCFixed<1>, // implicit flag
BCFixed<1>, // class-bounded?
+ BCFixed<1>, // value-bounded?
BCFixed<1>, // objc?
GenericEnvironmentIDField, // generic environment
AccessLevelField, // access level
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index cc321e7526..d8a4797276 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -2861,13 +2861,16 @@ bool EnumDecl::hasOnlyCasesWithoutAssociatedValues() const {
ProtocolDecl::ProtocolDecl(DeclContext *DC, SourceLoc ProtocolLoc,
SourceLoc NameLoc, Identifier Name,
+ SourceLoc NotClassLoc,
MutableArrayRef<TypeLoc> Inherited,
TrailingWhereClause *TrailingWhere)
: NominalTypeDecl(DeclKind::Protocol, DC, Name, NameLoc, Inherited,
nullptr),
- ProtocolLoc(ProtocolLoc), TrailingWhere(TrailingWhere) {
+ ProtocolLoc(ProtocolLoc),
+ NotClassLoc(NotClassLoc), TrailingWhere(TrailingWhere) {
ProtocolDeclBits.RequiresClassValid = false;
ProtocolDeclBits.RequiresClass = false;
+ ProtocolDeclBits.RequiresNonClass = false;
ProtocolDeclBits.ExistentialConformsToSelfValid = false;
ProtocolDeclBits.ExistentialConformsToSelf = false;
ProtocolDeclBits.Circularity
@@ -2957,34 +2960,40 @@ bool ProtocolDecl::inheritsFrom(const ProtocolDecl *super) const {
!= allProtocols.end();
}
-bool ProtocolDecl::requiresClassSlow() {
- // Set this first to catch (invalid) circular inheritance.
+bool ProtocolDecl::requiresClassNonClassSlow(bool wantsClass) {
+ // NOTE: The type checker will ensure that requiresClass and requiresNotClass
+ // are mutually exclusive.
+
+ // Set this first to ignore circular inheritance.
ProtocolDeclBits.RequiresClassValid = true;
- // Quick check: @objc protocols require a class.
- if (isObjC()) {
- ProtocolDeclBits.RequiresClass = true;
- return true;
- }
+ // These shouldn't be set yet.
+ assert(!ProtocolDeclBits.RequiresClass);
+ assert(!ProtocolDeclBits.RequiresNonClass);
+
+ // @objc protocols require a class.
+ ProtocolDeclBits.RequiresClass |= isObjC();
+
+ // Quick check
+ ProtocolDeclBits.RequiresNonClass |= NotClassLoc.isValid();
// Otherwise, check if the inheritance clause contains a
// class-constrained existential.
//
// FIXME: Use the requirement signature if available.
- ProtocolDeclBits.RequiresClass = false;
for (auto inherited : getInherited()) {
auto type = inherited.getType();
assert(type && "Should have type checked inheritance clause by now");
- if (type->isExistentialType()) {
- auto layout = type->getExistentialLayout();
- if (layout.requiresClass()) {
- ProtocolDeclBits.RequiresClass = true;
- return true;
- }
- }
+ if (!type->isExistentialType())
+ continue;
+ auto layout = type->getExistentialLayout();
+ ProtocolDeclBits.RequiresClass |= layout.requiresClass();
+ ProtocolDeclBits.RequiresNonClass |= layout.requiresNonClass();
}
- return ProtocolDeclBits.RequiresClass;
+ if (wantsClass)
+ return ProtocolDeclBits.RequiresClass;
+ return ProtocolDeclBits.RequiresNonClass;
}
bool ProtocolDecl::existentialConformsToSelfSlow() {
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 6cb983d971..cfabbe4024 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -306,6 +306,15 @@ bool ExistentialLayout::requiresClass() const {
return false;
}
+bool ExistentialLayout::requiresNonClass() const {
+ for (auto proto : getProtocols()) {
+ if (proto->requiresNonClass())
+ return true;
+ }
+
+ return false;
+}
+
bool ExistentialLayout::isAnyObject() const {
return (hasExplicitAnyObject && !superclass && getProtocols().empty());
}
@@ -2688,6 +2697,16 @@ bool ArchetypeType::requiresClass() const {
return false;
}
+bool ArchetypeType::requiresNonClass() const {
+ if (auto layout = getLayoutConstraint())
+ if (layout->isTrivial())
+ return true;
+ for (ProtocolDecl *conformed : getConformsTo())
+ if (conformed->requiresNonClass())
+ return true;
+ return false;
+}
+
namespace {
/// \brief Function object that orders archetypes by name.
struct OrderArchetypeByName {
@@ -2849,6 +2868,10 @@ bool ProtocolType::requiresClass() {
return getDecl()->requiresClass();
}
+bool ProtocolType::requiresNonClass() {
+ return getDecl()->requiresNonClass();
+}
+
bool ProtocolCompositionType::requiresClass() {
return getExistentialLayout().requiresClass();
}
diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp
index 27f152baf0..1d3e8740db 100644
--- a/lib/ClangImporter/ImportDecl.cpp
+++ b/lib/ClangImporter/ImportDecl.cpp
@@ -4177,7 +4177,7 @@ namespace {
auto result = Impl.createDeclWithClangNode<ProtocolDecl>(
decl, AccessLevel::Public, dc,
Impl.importSourceLoc(decl->getLocStart()),
- Impl.importSourceLoc(decl->getLocation()), name, None,
+ Impl.importSourceLoc(decl->getLocation()), name, SourceLoc(), None,
/*TrailingWhere=*/nullptr);
result->computeType();
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 788e357032..de0fe33cbc 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -2656,15 +2656,51 @@ ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
/// \endverbatim
ParserStatus Parser::parseInheritance(SmallVectorImpl<TypeLoc> &Inherited,
bool allowClassRequirement,
- bool allowAnyObject) {
+ bool allowAnyObject,
+ SourceLoc *notClassLoc) {
Scope S(this, ScopeKind::InheritanceClause);
consumeToken(tok::colon);
SourceLoc classRequirementLoc;
+ SourceLoc notClassRequirementLoc;
ParserStatus Status;
SourceLoc prevComma;
do {
+ // Parse the '!class' keyword for a value-type requirement.
+ if (Tok.is(tok::oper_prefix) && Tok.getText() == "!" &&
+ peekToken().is(tok::kw_class)) {
+ auto currentNotClassLoc = consumeToken();
+ auto classLoc = consumeToken();
+
+ if (!notClassLoc) {
+ diagnose(consumeToken(), diag::unexpected_not_class_constraint);
+ continue;
+ }
+
+ // If we already saw a !class requirement, complain.
+ if (notClassRequirementLoc.isValid()) {
+ diagnose(currentNotClassLoc, diag::redundant_not_class_requirement)
+ .highlight(notClassRequirementLoc)
+ .fixItRemove(SourceRange(prevComma, classLoc));
+ continue;
+ }
+
+ // If the !class requirement was not the first requirement, complain.
+ if (!Inherited.empty()) {
+ SourceLoc properLoc = Inherited[0].getSourceRange().Start;
+ diagnose(currentNotClassLoc, diag::late_not_class_requirement)
+ .fixItInsert(properLoc, "!class, ")
+ .fixItRemove(SourceRange(prevComma, classLoc));
+ continue;
+ }
+
+ // Record the location of the '!class' keyword.
+ notClassRequirementLoc = currentNotClassLoc;
+
+ *notClassLoc = currentNotClassLoc;
+ continue;
+ }
// Parse the 'class' keyword for a class requirement.
if (Tok.is(tok::kw_class)) {
// If we aren't allowed to have a class requirement here, complain.
@@ -2936,7 +2972,8 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) {
if (Tok.is(tok::colon))
status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
- /*allowAnyObject=*/false);
+ /*allowAnyObject=*/false,
+ /*notClassLoc*/nullptr);
// Parse the optional where-clause.
TrailingWhereClause *trailingWhereClause = nullptr;
@@ -3277,7 +3314,8 @@ ParserResult<TypeDecl> Parser::parseDeclAssociatedType(Parser::ParseDeclOptions
if (Tok.is(tok::colon))
Status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
- /*allowAnyObject=*/true);
+ /*allowAnyObject=*/true,
+ /*notClassLoc*/nullptr);
ParserResult<TypeRepr> UnderlyingTy;
if (Tok.is(tok::equal)) {
@@ -5029,7 +5067,8 @@ ParserResult<EnumDecl> Parser::parseDeclEnum(ParseDeclOptions Flags,
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
- /*allowAnyObject=*/false);
+ /*allowAnyObject=*/false,
+ /*notClassLoc*/nullptr);
ED->setInherited(Context.AllocateCopy(Inherited));
}
@@ -5291,7 +5330,8 @@ ParserResult<StructDecl> Parser::parseDeclStruct(ParseDeclOptions Flags,
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
- /*allowAnyObject=*/false);
+ /*allowAnyObject=*/false,
+ /*notClassLoc*/nullptr);
SD->setInherited(Context.AllocateCopy(Inherited));
}
@@ -5378,7 +5418,8 @@ ParserResult<ClassDecl> Parser::parseDeclClass(SourceLoc ClassLoc,
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
- /*allowAnyObject=*/false);
+ /*allowAnyObject=*/false,
+ /*notClassLoc*/nullptr);
CD->setInherited(Context.AllocateCopy(Inherited));
}
@@ -5463,11 +5504,13 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) {
// Parse optional inheritance clause.
SmallVector<TypeLoc, 4> InheritedProtocols;
SourceLoc colonLoc;
+ SourceLoc notClassLoc;
if (Tok.is(tok::colon)) {
colonLoc = Tok.getLoc();
Status |= parseInheritance(InheritedProtocols,
/*allowClassRequirement=*/true,
- /*allowAnyObject=*/true);
+ /*allowAnyObject=*/true,
+ ¬ClassLoc);
}
TrailingWhereClause *TrailingWhere = nullptr;
@@ -5481,6 +5524,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) {
ProtocolDecl *Proto = new (Context)
ProtocolDecl(CurDeclContext, ProtocolLoc, NameLoc, ProtocolName,
+ notClassLoc,
Context.AllocateCopy(InheritedProtocols), TrailingWhere);
// No need to setLocalDiscriminator: protocols can't appear in local contexts.
diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp
index 77b4566263..03c457295d 100644
--- a/lib/Sema/TypeCheckConstraints.cpp
+++ b/lib/Sema/TypeCheckConstraints.cpp
@@ -2800,6 +2800,9 @@ bool TypeChecker::isSubstitutableFor(Type type, ArchetypeType *archetype,
!type->isObjCExistentialType())
return false;
+ if (archetype->requiresNonClass() && type->mayHaveSuperclass())
+ return false;
+
if (auto superclass = archetype->getSuperclass()) {
if (!superclass->isExactSuperclassOf(type))
return false;
diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp
index a4050b4700..7d92f525f7 100644
--- a/lib/Sema/TypeCheckDecl.cpp
+++ b/lib/Sema/TypeCheckDecl.cpp
@@ -626,10 +626,18 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
}
if (auto proto = dyn_cast<ProtocolDecl>(decl)) {
- // Check for circular inheritance.
+ // Check for circular inheritance and class versus !class conflicts
+ auto boundConflict = proto->requiresClass() && proto->requiresNonClass();
+ if (boundConflict)
+ diagnose(proto, diag::class_vs_not_class_confict);
// FIXME: The diagnostics here should be improved.
bool diagnosedCircularity = false;
for (unsigned i = 0, n = allProtocols.size(); i != n; /*in loop*/) {
+ if (boundConflict && (allProtocols[i]->requiresClass() ||
+ allProtocols[i]->requiresNonClass()))
+ diagnose(allProtocols[i], diag::protocol_here,
+ allProtocols[i]->getName());
+
if (allProtocols[i] == proto || allProtocols[i]->inheritsFrom(proto)) {
if (!diagnosedCircularity) {
diagnose(proto, diag::circular_protocol_def, proto->getName().str());
diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp
index 439ea5da09..50d6a11b06 100644
--- a/lib/Sema/TypeCheckProtocol.cpp
+++ b/lib/Sema/TypeCheckProtocol.cpp
@@ -2069,6 +2069,14 @@ namespace {
return conformance;
}
+ // If the protocol requires a value type, classes are a non-starter.
+ if (Proto->requiresNonClass() && canT->getClassOrBoundGenericClass()) {
+ TC.diagnose(ComplainLoc,diag::class_cannot_conform_to_value_type_protocol,
+ T, Proto->getDeclaredType());
+ conformance->setInvalid();
+ return conformance;
+ }
+
// Foreign classes cannot conform to objc protocols.
if (Proto->isObjC()) {
if (auto clas = canT->getClassOrBoundGenericClass()) {
diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp
index ac2f666747..356664f11a 100644
--- a/lib/Serialization/Deserialization.cpp
+++ b/lib/Serialization/Deserialization.cpp
@@ -3037,13 +3037,14 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
case decls_block::PROTOCOL_DECL: {
IdentifierID nameID;
DeclContextID contextID;
- bool isImplicit, isClassBounded, isObjC;
+ bool isImplicit, isClassBounded, isValueBounded, isObjC;
GenericEnvironmentID genericEnvID;
uint8_t rawAccessLevel;
ArrayRef<uint64_t> rawInheritedIDs;
decls_block::ProtocolLayout::readRecord(scratch, nameID, contextID,
- isImplicit, isClassBounded, isObjC,
+ isImplicit, isClassBounded,
+ isValueBounded, isObjC,
genericEnvID, rawAccessLevel,
rawInheritedIDs);
@@ -3052,11 +3053,13 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
return declOrOffset;
auto proto = createDecl<ProtocolDecl>(DC, SourceLoc(), SourceLoc(),
- getIdentifier(nameID), None,
+ getIdentifier(nameID), SourceLoc(),
+ None,
/*TrailingWhere=*/nullptr);
declOrOffset = proto;
proto->setRequiresClass(isClassBounded);
+ proto->setRequiresNonClass(isValueBounded);
if (auto accessLevel = getActualAccessLevel(rawAccessLevel)) {
proto->setAccess(*accessLevel);
diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp
index 9385ccde54..b8d43cd472 100644
--- a/lib/Serialization/Serialization.cpp
+++ b/lib/Serialization/Serialization.cpp
@@ -2853,6 +2853,8 @@ void Serializer::writeDecl(const Decl *D) {
proto->isImplicit(),
const_cast<ProtocolDecl *>(proto)
->requiresClass(),
+ const_cast<ProtocolDecl *>(proto)
+ ->requiresNonClass(),
proto->isObjC(),
addGenericEnvironmentRef(
proto->getGenericEnvironment()),
diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb
index 8fa13f3131..7d52686755 100644
--- a/stdlib/public/core/Integers.swift.gyb
+++ b/stdlib/public/core/Integers.swift.gyb
@@ -970,7 +970,7 @@ def unsafeOperationComment(operator):
/// the required mutating methods. Extensions to `Numeric` provide default
/// implementations for the protocol's nonmutating methods based on the
/// mutating variants.
-public protocol Numeric : Equatable, ExpressibleByIntegerLiteral {
+public protocol Numeric : !class, Equatable, ExpressibleByIntegerLiteral {
/// Creates a new instance from the given integer, if it can be represented
/// exactly.
///
diff --git a/test/Sema/value_type_bound_protocol.swift b/test/Sema/value_type_bound_protocol.swift
new file mode 100644
index 0000000000..b3363c0034
--- /dev/null
+++ b/test/Sema/value_type_bound_protocol.swift
@@ -0,0 +1,12 @@
+// RUN: %target-typecheck-verify-swift
+
+protocol P : !class {} // expected-note {{protocol 'P' declared here}}
+struct S : P {}
+enum E : P {}
+class C : P {} // expected-error{{class type 'C' cannot conform to value type protocol 'P'}}
+
+protocol Bad_Double : !class, !class {} // expected-error {{redundant '!class' requirement}}
+protocol Bad_Late : P, !class {} // expected-error {{'!class' must come first in the requirement list}}
+
+protocol Pc : class {} // expected-note {{protocol 'Pc' declared here}}
+protocol Bad_Conflict : P, Pc {} // expected-error {{cannot inherit from both class bound and value-type bound protocols}}
diff --git a/test/Sema/value_type_bound_protocol_objc.swift b/test/Sema/value_type_bound_protocol_objc.swift
new file mode 100644
index 0000000000..aa298b3a52
--- /dev/null
+++ b/test/Sema/value_type_bound_protocol_objc.swift
@@ -0,0 +1,6 @@
+// RUN: %target-typecheck-verify-swift
+// REQUIRES: objc_interop
+
+import Foundation
+
+ at objc protocol Bad_ObjC : !class {} // expected-error {{cannot inherit from both class bound and value-type bound protocols}}
More information about the swift-dev
mailing list