[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,
+                               &notClassLoc);
   }
 
   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