From 01864d9af45e9ca2ed6533ab7c1f8a50d40f1920 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 11 Mar 2024 15:24:04 -0700 Subject: [PATCH] Sema: replace legacy Copyable containment check We can use part of the new infrastructure if we simply handle abstract conformances to Copyable, which is what we'd get upon lookup for a nominal in the old world. This means that we can merge diagnostics for the containment test together, and fix differences with deinit diagnostics. --- include/swift/AST/DiagnosticsSema.def | 7 -- lib/Parse/ParseDecl.cpp | 4 +- lib/Sema/MiscDiagnostics.cpp | 104 ++++--------------------- lib/Sema/MiscDiagnostics.h | 3 +- lib/Sema/TypeCheckDeclPrimary.cpp | 17 ++-- lib/Sema/TypeCheckInvertible.cpp | 30 ++++--- lib/Sema/TypeCheckInvertible.h | 4 +- lib/Sema/TypeCheckProtocol.cpp | 4 +- test/ASTGen/types.swift | 3 +- test/Parse/init_deinit.swift | 8 +- test/Parse/inverses_legacy.swift | 1 + test/Parse/inverses_legacy_ifdef.swift | 1 + test/Sema/copyable.swift | 4 +- test/Sema/moveonly_restrictions.swift | 46 +++++++---- 14 files changed, 84 insertions(+), 152 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 6c0db97e71121..45ad276b4ffd5 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -7674,13 +7674,6 @@ ERROR(bitwise_copyable_outside_module,none, (const ValueDecl *)) // -- older ones below -- - -ERROR(noncopyable_within_copyable, none, - "%kind0 cannot contain a noncopyable type without also being noncopyable", - (const ValueDecl *)) -NOTE(noncopyable_within_copyable_location, none, - "contained noncopyable %0 '%1.%2'", - (DescriptiveDeclKind, StringRef, StringRef)) ERROR(noncopyable_cannot_conform_to_type, none, "noncopyable %kind0 cannot conform to %1", (const ValueDecl *, Type)) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index debb4becfad80..21abc867677a9 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -10042,8 +10042,8 @@ parseDeclDeinit(ParseDeclOptions Flags, DeclAttributes &Attributes) { // Reject 'destructor' functions outside of structs, enums, classes, or // extensions that provide objc implementations. // - // Later in the type checker, we validate that structs/enums only do this if - // they are move only and that @objcImplementations are main-body. + // Later in the type checker, we validate that structs/enums are noncopyable + // and that @objcImplementations are main-body. auto rejectDestructor = [](DeclContext *dc) { if (isa(dc) || isa(dc) || isa(dc)) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 8b204f5dea929..47a89084f54f1 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -17,6 +17,7 @@ #include "MiscDiagnostics.h" #include "TypeCheckAvailability.h" #include "TypeCheckConcurrency.h" +#include "TypeCheckInvertible.h" #include "TypeChecker.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/DiagnosticsSema.h" @@ -6313,103 +6314,24 @@ bool swift::diagnoseUnhandledThrowsInAsyncContext(DeclContext *dc, return false; } -//===----------------------------------------------------------------------===// -// Copyable Type Containing Move Only Type Visitor -//===----------------------------------------------------------------------===// +// If we haven't enabled the NoncopyableGenerics feature, force a +// Copyable conformance check now. +void swift::forceCopyableConformanceCheckIfNeeded(NominalTypeDecl *nom) { + auto &ctx = nom->getASTContext(); -void swift::diagnoseCopyableTypeContainingMoveOnlyType( - NominalTypeDecl *copyableNominalType) { - auto &ctx = copyableNominalType->getASTContext(); if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) return; // taken care of in conformance checking - // If we already have a move only type, just bail, we have no further work to - // do. - if (!copyableNominalType->canBeCopyable()) - return; - - LLVM_DEBUG(llvm::dbgs() << "DiagnoseCopyableType for: " - << copyableNominalType->getName() << '\n'); - - auto &DE = copyableNominalType->getASTContext().Diags; - auto emitError = [©ableNominalType, - &DE](PointerUnion - topFieldToError, - DeclBaseName parentName, DescriptiveDeclKind fieldKind, - DeclBaseName fieldName) { - assert(!topFieldToError.isNull()); - if (auto *eltDecl = topFieldToError.dyn_cast()) { - DE.diagnoseWithNotes( - copyableNominalType->diagnose( - diag::noncopyable_within_copyable, - copyableNominalType), - [&]() { - eltDecl->diagnose( - diag:: - noncopyable_within_copyable_location, - fieldKind, parentName.userFacingName(), - fieldName.userFacingName()); - }); - return; - } + auto *copyable = ctx.getProtocol(KnownProtocolKind::Copyable); + Type selfTy = nom->getDeclaredInterfaceType(); - auto *varDecl = topFieldToError.get(); - DE.diagnoseWithNotes( - copyableNominalType->diagnose( - diag::noncopyable_within_copyable, - copyableNominalType), - [&]() { - varDecl->diagnose( - diag::noncopyable_within_copyable_location, - fieldKind, parentName.userFacingName(), - fieldName.userFacingName()); - }); - }; + auto conformanceRef = nom->getModuleContext()->lookupConformance(selfTy, + copyable, + /*allowMissing=*/false); - // If we have a struct decl... - if (auto *structDecl = dyn_cast(copyableNominalType)) { - // Visit each of the stored property var decls of the struct decl... - for (auto *fieldDecl : structDecl->getStoredProperties()) { - LLVM_DEBUG(llvm::dbgs() - << "Visiting struct field: " << fieldDecl->getName() << '\n'); - if (!fieldDecl->getInterfaceType()->isNoncopyable()) - continue; - emitError(fieldDecl, structDecl->getBaseName(), - fieldDecl->getDescriptiveKind(), fieldDecl->getBaseName()); - } - // We completed our checking, just return. + // it's never copyable + if (conformanceRef.isInvalid()) return; - } - - if (auto *enumDecl = dyn_cast(copyableNominalType)) { - // If we have an enum but we don't have any elements, just continue, we - // have nothing to check. - if (enumDecl->getAllElements().empty()) - return; - - // Otherwise for each element... - for (auto *enumEltDecl : enumDecl->getAllElements()) { - // If the element doesn't have any associated values, we have nothing to - // check, so continue. - if (!enumEltDecl->hasAssociatedValues()) - continue; - LLVM_DEBUG(llvm::dbgs() << "Visiting enum elt decl: " - << enumEltDecl->getName() << '\n'); - - // Otherwise, we have a case and need to check the types of the - // parameters of the case payload. - for (auto payloadParam : *enumEltDecl->getParameterList()) { - LLVM_DEBUG(llvm::dbgs() << "Visiting payload param: " - << payloadParam->getName() << '\n'); - if (payloadParam->getInterfaceType()->isNoncopyable()) { - emitError(enumEltDecl, enumDecl->getBaseName(), - enumEltDecl->getDescriptiveKind(), - enumEltDecl->getBaseName()); - } - } - } - // We have finished processing this enum... so return. - return; - } + swift::checkCopyableConformance(nom, conformanceRef); } diff --git a/lib/Sema/MiscDiagnostics.h b/lib/Sema/MiscDiagnostics.h index 4e336b9215a2a..990e023318a13 100644 --- a/lib/Sema/MiscDiagnostics.h +++ b/lib/Sema/MiscDiagnostics.h @@ -158,8 +158,7 @@ namespace swift { static bool shouldWalkIntoDeclInClosureContext(Decl *D); }; -void diagnoseCopyableTypeContainingMoveOnlyType( - NominalTypeDecl *copyableNominalType); +void forceCopyableConformanceCheckIfNeeded(NominalTypeDecl *nominal); } // namespace swift diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 181f2d00d3a1e..59eaf614be7a4 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -3055,7 +3055,7 @@ class DeclChecker : public DeclVisitor { ED->diagnose(diag::noncopyable_objc_enum); } // FIXME(kavon): see if these can be integrated into other parts of Sema - diagnoseCopyableTypeContainingMoveOnlyType(ED); + forceCopyableConformanceCheckIfNeeded(ED); diagnoseIncompatibleProtocolsForMoveOnlyType(ED); checkExplicitAvailability(ED); @@ -3117,7 +3117,7 @@ class DeclChecker : public DeclVisitor { // If this struct is not move only, check that all vardecls of nominal type // are not move only. - diagnoseCopyableTypeContainingMoveOnlyType(SD); + forceCopyableConformanceCheckIfNeeded(SD); diagnoseIncompatibleProtocolsForMoveOnlyType(SD); } @@ -4130,19 +4130,12 @@ class DeclChecker : public DeclVisitor { } void visitDestructorDecl(DestructorDecl *DD) { - // Only check again for destructor decl outside of a class if our destructor - // is not marked as invalid. + // Only check again for destructor decl outside of a struct/enum/class + // if our destructor is not marked as invalid. if (!DD->isInvalid()) { auto *nom = dyn_cast( DD->getDeclContext()->getImplementedObjCContext()); - if (!nom || isa(nom)) { - DD->diagnose(diag::destructor_decl_outside_class_or_noncopyable); - - } else if (!Ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics) - && !isa(nom) - && nom->canBeCopyable()) { - // When we have NoncopyableGenerics, deinits get validated as part of - // Copyable-conformance checking. + if (!nom || !isa(nom)) { DD->diagnose(diag::destructor_decl_outside_class_or_noncopyable); } diff --git a/lib/Sema/TypeCheckInvertible.cpp b/lib/Sema/TypeCheckInvertible.cpp index cef7998e85fa5..71a29ff4f9463 100644 --- a/lib/Sema/TypeCheckInvertible.cpp +++ b/lib/Sema/TypeCheckInvertible.cpp @@ -108,11 +108,12 @@ static void tryEmitContainmentFixits(NominalTypeDecl *enclosingNom, /// MARK: conformance checking static void checkInvertibleConformanceCommon(DeclContext *dc, - ProtocolConformance *conformance, + ProtocolConformanceRef conformance, InvertibleProtocolKind ip) { + assert(!conformance.isInvalid()); + const auto kp = getKnownProtocolKind(ip); - auto *proto = conformance->getProtocol(); - assert(proto->isSpecificProtocol(kp)); + assert(conformance.getRequirement()->isSpecificProtocol(kp)); auto *nominalDecl = dc->getSelfNominalTypeDecl(); assert(isa(nominalDecl) || @@ -133,17 +134,26 @@ static void checkInvertibleConformanceCommon(DeclContext *dc, bool hasExplicitInverse = inverses.contains(ip); - bool hasUnconditionalConformance = false; - auto *normalConf = dyn_cast(conformance); - if (normalConf && normalConf->getConditionalRequirements().empty()) - hasUnconditionalConformance = true; + bool hasUnconditionalConformance = conformance.isAbstract(); + SourceLoc conformanceLoc = nominalDecl->getLoc(); + + if (conformance.isConcrete()) { + auto concrete = conformance.getConcrete(); + if (auto *normalConf = dyn_cast(concrete)) { + hasUnconditionalConformance = + normalConf->getConditionalRequirements().empty(); + conformanceLoc = normalConf->getLoc(); + assert(conformanceLoc); + } + } + assert(!conformance.isPack() && "not handled"); if (!isa(nominalDecl) || ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses)) { // If the inheritance clause contains ~Copyable, reject an unconditional // conformance to Copyable. if (hasExplicitInverse && hasUnconditionalConformance) { - ctx.Diags.diagnose(normalConf->getLoc(), + ctx.Diags.diagnose(conformanceLoc, diag::inverse_but_also_conforms, nominalDecl, getProtocolName(kp)); } @@ -224,13 +234,13 @@ static void checkInvertibleConformanceCommon(DeclContext *dc, } void swift::checkEscapableConformance(DeclContext *dc, - ProtocolConformance *conformance) { + ProtocolConformanceRef conformance) { checkInvertibleConformanceCommon(dc, conformance, InvertibleProtocolKind::Escapable); } void swift::checkCopyableConformance(DeclContext *dc, - ProtocolConformance *conformance) { + ProtocolConformanceRef conformance) { checkInvertibleConformanceCommon(dc, conformance, InvertibleProtocolKind::Copyable); } diff --git a/lib/Sema/TypeCheckInvertible.h b/lib/Sema/TypeCheckInvertible.h index dc66fa0ecaa41..6e38a0e3d01a6 100644 --- a/lib/Sema/TypeCheckInvertible.h +++ b/lib/Sema/TypeCheckInvertible.h @@ -33,11 +33,11 @@ class StorageVisitor { /// Checks that all stored properties or associated values are Copyable. void checkCopyableConformance(DeclContext *dc, - ProtocolConformance *conformance); + ProtocolConformanceRef conformance); /// Checks that all stored properties or associated values are Escapable. void checkEscapableConformance(DeclContext *dc, - ProtocolConformance *conformance); + ProtocolConformanceRef conformance); } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index f289df1d29247..d65586837a7bc 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6119,10 +6119,10 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) { tryDiagnoseExecutorConformance(Context, nominal, proto); } else if (NoncopyableGenerics && proto->isSpecificProtocol(KnownProtocolKind::Copyable)) { - checkCopyableConformance(dc, conformance); + checkCopyableConformance(dc, ProtocolConformanceRef(conformance)); } else if (NoncopyableGenerics && proto->isSpecificProtocol(KnownProtocolKind::Escapable)) { - checkEscapableConformance(dc, conformance); + checkEscapableConformance(dc, ProtocolConformanceRef(conformance)); } else if (Context.LangOpts.hasFeature(Feature::BitwiseCopyable) && proto->isSpecificProtocol(KnownProtocolKind::BitwiseCopyable)) { checkBitwiseCopyableConformance( diff --git a/test/ASTGen/types.swift b/test/ASTGen/types.swift index 4624cb03fc67e..aa78d9c021d9f 100644 --- a/test/ASTGen/types.swift +++ b/test/ASTGen/types.swift @@ -46,7 +46,8 @@ func testRepeatEach(_ t: repeat each T) -> (repeat each T) { fatalError() } -struct FileDescriptor: ~Copyable { +// FIXME: this error isn't correct to emit. the parsing might be ignoring the ~ +struct FileDescriptor: ~Copyable { // expected-error {{struct 'FileDescriptor' required to be 'Copyable' but is marked with '~Copyable'}} var fd = 1 } diff --git a/test/Parse/init_deinit.swift b/test/Parse/init_deinit.swift index a49a1d660692b..21bb2ed48ee93 100644 --- a/test/Parse/init_deinit.swift +++ b/test/Parse/init_deinit.swift @@ -1,7 +1,5 @@ // RUN: %target-typecheck-verify-swift -// XFAIL: noncopyable_generics - struct FooStructConstructorA { init // expected-error {{expected '('}} // expected-error@-1{{initializer requires a body}} @@ -36,7 +34,7 @@ struct FooStructDeinitializerB { } struct FooStructDeinitializerC { - deinit {} // expected-error {{deinitializers may only be declared within a class, actor, or noncopyable type}} + deinit {} // expected-error {{deinitializer cannot be declared in struct 'FooStructDeinitializerC' that conforms to 'Copyable'}} } class FooClassDeinitializerA { @@ -61,7 +59,7 @@ deinit {} // expected-error {{deinitializers may only be declared within a class struct BarStruct { init() {} - deinit {} // expected-error {{deinitializers may only be declared within a class, actor, or noncopyable type}} + deinit {} // NOTE: this doesn't get diagnosed with the other errors in this file for some reason. } extension BarStruct { @@ -73,7 +71,7 @@ extension BarStruct { enum BarUnion { init() {} - deinit {} // expected-error {{deinitializers may only be declared within a class, actor, or noncopyable type}} + deinit {} // NOTE: this doesn't get diagnosed with the other errors in this file for some reason. } extension BarUnion { diff --git a/test/Parse/inverses_legacy.swift b/test/Parse/inverses_legacy.swift index 21c50174081ce..dfac49fd52daa 100644 --- a/test/Parse/inverses_legacy.swift +++ b/test/Parse/inverses_legacy.swift @@ -35,6 +35,7 @@ protocol Rope: ~Copyable { // expected-error {{cannot suppress conforma extension S: ~Copyable {} // expected-error {{cannot suppress conformances here}} // expected-error@-1 {{noncopyable struct 'S' cannot conform to 'Copyable'}} + // expected-error@-2 {{struct 'S' required to be 'Copyable' but is marked with '~Copyable'}} func takeNoncopyableGeneric(_ t: T) {} // expected-error {{cannot suppress conformances here}} diff --git a/test/Parse/inverses_legacy_ifdef.swift b/test/Parse/inverses_legacy_ifdef.swift index c29c5ccb120ca..35fb4bd93554c 100644 --- a/test/Parse/inverses_legacy_ifdef.swift +++ b/test/Parse/inverses_legacy_ifdef.swift @@ -40,6 +40,7 @@ extension NC: Compare { // expected-error {{noncopyable struct 'NC' cannot confo extension NC: Sortable { // expected-error {{noncopyable struct 'NC' cannot conform to 'Sortable'}} // expected-error@-1 {{type 'NC' does not conform to protocol 'Sortable'}} + // expected-error@-2 {{struct 'NC' required to be 'Copyable' but is marked with '~Copyable'}} typealias Element = NC // expected-note {{possibly intended match 'NC.Element' (aka 'NC') does not conform to 'Copyable'}} mutating func sort() { } diff --git a/test/Sema/copyable.swift b/test/Sema/copyable.swift index d8aca72b9e211..b7fa1c66f25f6 100644 --- a/test/Sema/copyable.swift +++ b/test/Sema/copyable.swift @@ -10,7 +10,9 @@ typealias WhatIfIQualify = Swift.Copyable class C: Copyable {} -@_moveOnly struct MOStruct: Copyable {} // expected-error {{noncopyable struct 'MOStruct' cannot conform to 'Copyable'}} +@_moveOnly struct MOStruct: Copyable {} +// expected-error@-1 {{noncopyable struct 'MOStruct' cannot conform to 'Copyable'}} +// expected-error@-2 {{struct 'MOStruct' required to be 'Copyable' but is marked with '~Copyable'}} func whatever(_ t: T) where T: Copyable {} diff --git a/test/Sema/moveonly_restrictions.swift b/test/Sema/moveonly_restrictions.swift index d64e783a5682a..c2921c742b3ae 100644 --- a/test/Sema/moveonly_restrictions.swift +++ b/test/Sema/moveonly_restrictions.swift @@ -7,12 +7,12 @@ class CopyableKlass {} @_moveOnly -class MoveOnlyKlass { +class MoveOnlyKlass { // expected-note 6{{class 'MoveOnlyKlass' has '~Copyable' constraint preventing 'Copyable' conformance}} init?() {} // expected-error {{noncopyable types cannot have failable initializers yet}} } @_moveOnly -class MoveOnlyStruct { +struct MoveOnlyStruct { // expected-note {{struct 'MoveOnlyStruct' has '~Copyable' constraint preventing 'Copyable' conformance}} init?(one: Bool) {} // expected-error {{noncopyable types cannot have failable initializers yet}} init!(two: Bool) {} // expected-error {{noncopyable types cannot have failable initializers yet}} } @@ -30,9 +30,9 @@ class CMoveOnly { var moveOnlyS: MoveOnlyStruct? = nil // expected-error {{noncopyable type 'MoveOnlyStruct' cannot be used with generic type 'Optional' yet}} } -struct OptionalGrandField { // expected-error {{generic struct 'OptionalGrandField' cannot contain a noncopyable type without also being noncopyable}} +struct OptionalGrandField { var moveOnly3: T? - var moveOnly2: MoveOnlyKlass // expected-note {{contained noncopyable property 'OptionalGrandField.moveOnly2'}} + var moveOnly2: MoveOnlyKlass // expected-error {{stored property 'moveOnly2' of 'Copyable'-conforming generic struct 'OptionalGrandField' has non-Copyable type 'MoveOnlyKlass'}} } struct S0 { @@ -44,10 +44,10 @@ struct SCopyable { var copyable: CopyableKlass } -struct S { // expected-error {{struct 'S' cannot contain a noncopyable type without also being noncopyable}} +struct S { var copyable: CopyableKlass var moveOnly2: MoveOnlyStruct? // expected-error {{noncopyable type 'MoveOnlyStruct' cannot be used with generic type 'Optional' yet}} - var moveOnly: MoveOnlyStruct // expected-note {{contained noncopyable property 'S.moveOnly'}} + var moveOnly: MoveOnlyStruct // expected-error {{stored property 'moveOnly' of 'Copyable'-conforming struct 'S' has non-Copyable type 'MoveOnlyStruct'}} var moveOnly3: OptionalGrandField // expected-error {{noncopyable type 'MoveOnlyKlass' cannot be used with generic type 'OptionalGrandField' yet}} var moveOnly3: OptionalGrandField // expected-error {{noncopyable type 'MoveOnlyStruct' cannot be used with generic type 'OptionalGrandField' yet}} } @@ -58,9 +58,9 @@ struct SMoveOnly { var moveOnly: MoveOnlyKlass } -enum E { // expected-error {{enum 'E' cannot contain a noncopyable type without also being noncopyable}} +enum E { case lhs(CopyableKlass) - case rhs(MoveOnlyKlass) // expected-note {{contained noncopyable enum case 'E.rhs'}} + case rhs(MoveOnlyKlass) // expected-error {{associated value 'rhs' of 'Copyable'-conforming enum 'E' has non-Copyable type 'MoveOnlyKlass'}} case rhs2(OptionalGrandField) // expected-error {{noncopyable type 'MoveOnlyKlass' cannot be used with generic type 'OptionalGrandField' yet}} } @@ -76,10 +76,14 @@ extension EMoveOnly { init!(three: Bool) {} // expected-error {{noncopyable types cannot have failable initializers yet}} } -extension MoveOnlyStruct { +extension MoveOnlyKlass { convenience init?(three: Bool) {} // expected-error {{noncopyable types cannot have failable initializers yet}} } +extension MoveOnlyStruct { + init?(three: Bool) {} // expected-error {{noncopyable types cannot have failable initializers yet}} +} + func foo() { class C2 { var copyable: CopyableKlass? = nil @@ -92,9 +96,9 @@ func foo() { var moveOnly: MoveOnlyKlass? = nil // expected-error {{noncopyable type 'MoveOnlyKlass' cannot be used with generic type 'Optional' yet}} } - struct S2 { // expected-error {{struct 'S2' cannot contain a noncopyable type without also being noncopyable}} + struct S2 { var copyable: CopyableKlass - var moveOnly: MoveOnlyKlass // expected-note {{contained noncopyable property 'S2.moveOnly'}} + var moveOnly: MoveOnlyKlass // expected-error {{stored property 'moveOnly' of 'Copyable'-conforming struct 'S2' has non-Copyable type 'MoveOnlyKlass'}} } @_moveOnly @@ -103,9 +107,9 @@ func foo() { var moveOnly: MoveOnlyKlass } - enum E2 { // expected-error {{enum 'E2' cannot contain a noncopyable type without also being noncopyable}} + enum E2 { case lhs(CopyableKlass) - case rhs(MoveOnlyKlass) // expected-note {{contained noncopyable enum case 'E2.rhs'}} + case rhs(MoveOnlyKlass) // expected-error {{associated value 'rhs' of 'Copyable'-conforming enum 'E2' has non-Copyable type 'MoveOnlyKlass'}} } @_moveOnly @@ -125,9 +129,9 @@ func foo() { var moveOnly: MoveOnlyKlass? = nil // expected-error {{noncopyable type 'MoveOnlyKlass' cannot be used with generic type 'Optional' yet}} } - struct S3 { // expected-error {{struct 'S3' cannot contain a noncopyable type without also being noncopyable}} + struct S3 { var copyable: CopyableKlass - var moveOnly: MoveOnlyKlass // expected-note {{contained noncopyable property 'S3.moveOnly'}} + var moveOnly: MoveOnlyKlass // expected-error {{stored property 'moveOnly' of 'Copyable'-conforming struct 'S3' has non-Copyable type 'MoveOnlyKlass'}} } @_moveOnly @@ -136,9 +140,9 @@ func foo() { var moveOnly: MoveOnlyKlass } - enum E3 { // expected-error {{enum 'E3' cannot contain a noncopyable type without also being noncopyable}} + enum E3 { case lhs(CopyableKlass) - case rhs(MoveOnlyKlass) // expected-note {{contained noncopyable enum case 'E3.rhs'}} + case rhs(MoveOnlyKlass) // expected-error {{associated value 'rhs' of 'Copyable'-conforming enum 'E3' has non-Copyable type 'MoveOnlyKlass'}} } @_moveOnly @@ -245,4 +249,12 @@ struct StructHolder { } } +struct BarStruct { + init() {} + deinit {} // expected-error {{deinitializer cannot be declared in struct 'BarStruct' that conforms to 'Copyable'}} +} +enum BarUnion { + init() {} + deinit {} // expected-error {{deinitializer cannot be declared in enum 'BarUnion' that conforms to 'Copyable'}} +}