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 + +@objc protocol Bad_ObjC : !class {} // expected-error {{cannot inherit from both class bound and value-type bound protocols}} _______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev