diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index 7aca39268496c..3517bd718c87f 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -815,7 +815,7 @@ SIMPLE_DECL_ATTR(_noObjCBridging, NoObjCBridging, UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 155) -SIMPLE_DECL_ATTR(_manualOwnership, ManualOwnership, +SIMPLE_DECL_ATTR(_noManualOwnership, NoManualOwnership, OnAbstractFunction | OnSubscript, UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 156) diff --git a/include/swift/AST/DiagnosticGroups.def b/include/swift/AST/DiagnosticGroups.def index 2ee8d7d848026..4263134a35cbd 100644 --- a/include/swift/AST/DiagnosticGroups.def +++ b/include/swift/AST/DiagnosticGroups.def @@ -49,6 +49,7 @@ GROUP(ConformanceIsolation, "conformance-isolation") GROUP(ForeignReferenceType, "foreign-reference-type") GROUP(DeprecatedDeclaration, "deprecated-declaration") GROUP(DynamicCallable, "dynamic-callable-requirements") +GROUP(DynamicExclusivity, "dynamic-exclusivity") GROUP(EmbeddedRestrictions, "embedded-restrictions") GROUP(ErrorInFutureSwiftVersion, "error-in-future-swift-version") GROUP(ExclusivityViolation, "exclusivity-violation") @@ -73,6 +74,7 @@ GROUP(ProtocolTypeNonConformance, "protocol-type-non-conformance") GROUP(RegionIsolation, "region-isolation") GROUP(ResultBuilderMethods, "result-builder-methods") GROUP(ReturnTypeImplicitCopy, "return-type-implicit-copy") +GROUP(SemanticCopies, "semantic-copies") GROUP(SendableClosureCaptures, "sendable-closure-captures") GROUP(SendableMetatypes, "sendable-metatypes") GROUP(SendingClosureRisksDataRace, "sending-closure-risks-data-race") diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 7f890c822f774..2dce5cb21ebdd 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -433,15 +433,22 @@ ERROR(wrong_linkage_for_serialized_function,none, "function has wrong linkage to be called from %0", (StringRef)) NOTE(performance_called_from,none, "called from here", ()) -ERROR(manualownership_copy,none, - "explicit 'copy' required here; please report this vague diagnostic as a bug", ()) -ERROR(manualownership_copy_happened,none, + +// ManualOwnership diagnostics +GROUPED_WARNING(manualownership_copy,SemanticCopies,DefaultIgnore, + "implicit 'copy' happens here; please report this vague diagnostic as a bug", ()) +GROUPED_WARNING(manualownership_copy_happened,SemanticCopies,DefaultIgnore, "accessing %0 may produce a copy; write 'copy' to acknowledge or 'consume' to elide", (Identifier)) -ERROR(manualownership_copy_demanded,none, +GROUPED_WARNING(manualownership_copy_demanded,SemanticCopies,DefaultIgnore, "independent copy of %0 is required here; write 'copy' to acknowledge or 'consume' to elide", (Identifier)) -ERROR(manualownership_copy_captured,none, +GROUPED_WARNING(manualownership_copy_captured,SemanticCopies,DefaultIgnore, "closure capture of '%0' requires independent copy of it; write [%0 = copy %0] in the closure's capture list to acknowledge", (StringRef)) +GROUPED_WARNING(manualownership_exclusivity,DynamicExclusivity,DefaultIgnore, + "exclusive access here will be checked at runtime; please report this vague diagnostic as a bug", ()) +GROUPED_WARNING(manualownership_exclusivity_named,DynamicExclusivity,DefaultIgnore, + "accessing %0 here may incur runtime exclusivity check%1", (Identifier, StringRef)) + // 'transparent' diagnostics ERROR(circular_transparent,none, "inlining 'transparent' functions forms circular loop", ()) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index fb908e1ed54ce..ee76990cb0812 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2145,9 +2145,7 @@ ERROR(attr_static_exclusive_only_mutating,none, ERROR(attr_static_exclusive_no_setters,none, "variable of type %0 must not have a setter", (Type)) -// @_manualOwnership -ERROR(attr_manual_ownership_experimental,none, - "'@_manualOwnership' requires '-enable-experimental-feature ManualOwnership'", ()) +// ManualOwnership ERROR(attr_manual_ownership_noimplicitcopy,none, "'@_noImplicitCopy' cannot be used with ManualOwnership", ()) diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index ec5aa3c27eeb0..6a8f21aef02ac 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -449,7 +449,9 @@ EXPERIMENTAL_FEATURE(LifetimeDependence, true) /// Enable the `@_staticExclusiveOnly` attribute. EXPERIMENTAL_FEATURE(StaticExclusiveOnly, true) -/// Enable the `@_manualOwnership` attribute. +/// Enable the ManualOwnership diagnostic groups: +/// * -Wwarning SemanticCopies +/// * -Wwarning DynamicExclusivity EXPERIMENTAL_FEATURE(ManualOwnership, false) /// Enable the @extractConstantsFromMembers attribute. diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index f09f04c6ca7ce..b62a6ec2ffe1d 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -5024,7 +5024,7 @@ class PrintAttribute : public AttributeVisitor, TRIVIAL_ATTR_PRINTER(NoLocks, no_locks) TRIVIAL_ATTR_PRINTER(NoMetadata, no_metadata) TRIVIAL_ATTR_PRINTER(NoObjCBridging, no_objc_bridging) - TRIVIAL_ATTR_PRINTER(ManualOwnership, manual_ownership) + TRIVIAL_ATTR_PRINTER(NoManualOwnership, no_manual_ownership) TRIVIAL_ATTR_PRINTER(NoRuntime, no_runtime) TRIVIAL_ATTR_PRINTER(NonEphemeral, non_ephemeral) TRIVIAL_ATTR_PRINTER(NonEscapable, non_escapable) diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 526ae4394f6f9..5e66153edcd3e 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -265,7 +265,6 @@ extension ASTGenVisitor { .LexicalLifetimes, .LLDBDebuggerFunction, .MainType, - .ManualOwnership, .Marker, .MoveOnly, .NeverEmitIntoClient, @@ -276,6 +275,7 @@ extension ASTGenVisitor { .NoRuntime, .NoImplicitCopy, .NoLocks, + .NoManualOwnership, .NoMetadata, .NoObjCBridging, .NonEphemeral, diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index ad47d221d9b0f..64b23a9e47523 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -208,7 +208,9 @@ void SILFunctionBuilder::addFunctionAttributes( F->setPerfConstraints(PerformanceConstraints::NoExistentials); } else if (Attrs.hasAttribute()) { F->setPerfConstraints(PerformanceConstraints::NoObjCBridging); - } else if (Attrs.hasAttribute()) { + } else if (M.getASTContext().LangOpts.hasFeature(Feature::ManualOwnership) && + constant && constant.hasDecl() && !constant.isImplicit() && + !Attrs.hasAttribute()) { F->setPerfConstraints(PerformanceConstraints::ManualOwnership); } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 6c9d3d38c5848..d82ca3e3e2606 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1368,15 +1368,15 @@ void SILGenModule::preEmitFunction(SILDeclRef constant, SILFunction *F, // // If ManualOwnership ends up subsuming those prior mechanisms for an // explicit-copy mode, we can move this somewhere else, like postEmitFunction. - if (auto *ace = constant.getAbstractClosureExpr()) { - if (auto *dc = ace->getOutermostFunctionContext()) { - if (auto *decl = dc->getAsDecl()) { - if (decl->getAttrs().hasAttribute()) { - F->setPerfConstraints(PerformanceConstraints::ManualOwnership); - } - } - } - } + // + // FIXME: maybe this should happen in SILFunctionBuilder::getOrCreateFunction + if (getASTContext().LangOpts.hasFeature(Feature::ManualOwnership)) + if (auto *ace = constant.getAbstractClosureExpr()) + if (auto *dc = ace->getOutermostFunctionContext()) + if (auto *decl = dc->getAsDecl()) + if (!decl->isImplicit() && + !decl->getAttrs().hasAttribute()) + F->setPerfConstraints(PerformanceConstraints::ManualOwnership); LLVM_DEBUG(llvm::dbgs() << "lowering "; F->printName(llvm::dbgs()); diff --git a/lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp b/lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp index d76adf18daf96..7967d3f658b81 100644 --- a/lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp +++ b/lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp @@ -489,6 +489,49 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst, return false; } } + if (impact & RuntimeEffect::ExclusivityChecking) { + switch (inst->getKind()) { + case SILInstructionKind::BeginUnpairedAccessInst: + case SILInstructionKind::EndUnpairedAccessInst: + // These instructions are quite unusual; they seem to only ever created + // explicitly by calling functions from the Builtin module, see: + // - emitBuiltinPerformInstantaneousReadAccess + // - emitBuiltinEndUnpairedAccess + break; + case SILInstructionKind::EndAccessInst: + break; // We'll already diagnose the begin access. + case SILInstructionKind::BeginAccessInst: { + auto bai = cast(inst); + auto info = VariableNameInferrer::inferNameAndRoot(bai->getSource()); + + if (!info) { + LLVM_DEBUG(llvm::dbgs() << "exclusivity (no name?): " << *inst); + diagnose(loc, diag::manualownership_exclusivity); + return false; + } + Identifier name = info->first; + SILValue root = info->second; + StringRef advice = ""; + + // Try to classify the root to give advice. + if (isa(root)) { + advice = ", because it involves a global variable"; + } else if (root->getType().isAnyClassReferenceType()) { + advice = ", because it's a member of a reference type"; + } + + LLVM_DEBUG(llvm::dbgs() << "exclusivity: " << *inst); + LLVM_DEBUG(llvm::dbgs() << "with root: " << root); + + diagnose(loc, diag::manualownership_exclusivity_named, name, advice); + break; + } + default: + LLVM_DEBUG(llvm::dbgs() << "UNKNOWN EXCLUSIVITY INST: " << *inst); + diagnose(loc, diag::manualownership_exclusivity); + return false; + } + } return false; } diff --git a/lib/SILOptimizer/Utils/VariableNameUtils.cpp b/lib/SILOptimizer/Utils/VariableNameUtils.cpp index 2273f67f8c590..da467e7ab1783 100644 --- a/lib/SILOptimizer/Utils/VariableNameUtils.cpp +++ b/lib/SILOptimizer/Utils/VariableNameUtils.cpp @@ -651,6 +651,7 @@ SILValue VariableNameInferrer::findDebugInfoProvidingValueHelper( isa(searchValue) || isa(searchValue) || isa(searchValue) || isa(searchValue) || isa(searchValue) || + isa(searchValue) || isa(searchValue) || isa(searchValue) || isa(searchValue) || diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 28748450b7f1f..562f88164f629 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -183,6 +183,7 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(NoAllocation) IGNORED_ATTR(NoRuntime) IGNORED_ATTR(NoExistentials) + IGNORED_ATTR(NoManualOwnership) IGNORED_ATTR(NoObjCBridging) IGNORED_ATTR(EmitAssemblyVisionRemarks) IGNORED_ATTR(ShowInInterface) @@ -476,7 +477,6 @@ class AttributeChecker : public AttributeVisitor { void visitUnsafeNonEscapableResultAttr(UnsafeNonEscapableResultAttr *attr); void visitStaticExclusiveOnlyAttr(StaticExclusiveOnlyAttr *attr); - void visitManualOwnershipAttr(ManualOwnershipAttr *attr); void visitWeakLinkedAttr(WeakLinkedAttr *attr); void visitSILGenNameAttr(SILGenNameAttr *attr); void visitLifetimeAttr(LifetimeAttr *attr); @@ -8408,13 +8408,6 @@ void AttributeChecker::visitStaticExclusiveOnlyAttr( } } -void AttributeChecker::visitManualOwnershipAttr(ManualOwnershipAttr *attr) { - if (Ctx.LangOpts.hasFeature(Feature::ManualOwnership)) - return; - - diagnoseAndRemoveAttr(attr, diag::attr_manual_ownership_experimental); -} - void AttributeChecker::visitWeakLinkedAttr(WeakLinkedAttr *attr) { if (!Ctx.LangOpts.Target.isOSBinFormatCOFF()) return; diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 7fd7a8bd03d93..5861b1b0dfbfa 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1620,8 +1620,8 @@ namespace { UNINTERESTING_ATTR(NoAllocation) UNINTERESTING_ATTR(NoRuntime) UNINTERESTING_ATTR(NoExistentials) + UNINTERESTING_ATTR(NoManualOwnership) UNINTERESTING_ATTR(NoObjCBridging) - UNINTERESTING_ATTR(ManualOwnership) UNINTERESTING_ATTR(Inlinable) UNINTERESTING_ATTR(Effects) UNINTERESTING_ATTR(Expose) diff --git a/test/SIL/manual_ownership.swift b/test/SIL/manual_ownership.swift index e4180306bbd14..a12bc1ba279c8 100644 --- a/test/SIL/manual_ownership.swift +++ b/test/SIL/manual_ownership.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend %s -emit-sil -verify \ -// RUN: -enable-experimental-feature ManualOwnership +// RUN: -enable-experimental-feature ManualOwnership \ +// RUN: -Wwarning SemanticCopies // REQUIRES: swift_feature_ManualOwnership @@ -13,7 +14,6 @@ public struct Pair { var x: Int var y: Int - @_manualOwnership consuming func midpoint(_ other: borrowing Pair) -> Pair { return Pair(x: (x + other.x) / 2, y: (y + other.y) / 2) } @@ -26,10 +26,8 @@ public class Triangle { var nontrivial = Whatever() - @_manualOwnership consuming func consuming() {} - @_manualOwnership borrowing func borrowing() {} } @@ -43,51 +41,41 @@ func borrow_generic(_ t: borrowing T) {} /// MARK: return statements -@_manualOwnership public func basic_return1() -> Triangle { let x = Triangle() return x } -@_manualOwnership public func basic_return2(t: Triangle) -> Triangle { - return t // expected-error {{independent copy of 't' is required here; write 'copy' to acknowledge or 'consume' to elide}} + return t // expected-warning {{independent copy of 't' is required here; write 'copy' to acknowledge or 'consume' to elide}} } -@_manualOwnership public func basic_return2_fixed(t: Triangle) -> Triangle { return copy t } -@_manualOwnership public func basic_return3() -> Triangle { return Triangle() } -@_manualOwnership func return_borrowed(_ t: borrowing Triangle) -> Triangle { - return t // expected-error {{independent copy of 't' is required here; write 'copy' to acknowledge or 'consume' to elide}} + return t // expected-warning {{independent copy of 't' is required here; write 'copy' to acknowledge or 'consume' to elide}} } -@_manualOwnership func return_borrowed_fixed(_ t: borrowing Triangle) -> Triangle { return copy t } // FIXME: there's no workaround to this; it acts like a var so it's the same class of problem (rdar://161359163) -@_manualOwnership func return_consumingParam(_ t: consuming Triangle) -> Triangle { - return t // expected-error {{independent copy of 't' is required here; write 'copy' to acknowledge or 'consume' to elide}} + return t // expected-warning {{independent copy of 't' is required here; write 'copy' to acknowledge or 'consume' to elide}} } -@_manualOwnership func return_consumingParam_no_workaround(_ t: consuming Triangle) -> Triangle { return copy t } -@_manualOwnership func return_owned(_ t: __owned Triangle) -> Triangle { return t } -@_manualOwnership func reassign_with_lets() -> Triangle { let x = Triangle() let y = x @@ -95,16 +83,14 @@ func reassign_with_lets() -> Triangle { return z } -@_manualOwnership func renamed_return(_ cond: Bool, _ a: Triangle) -> Triangle { let b = a let c = b // FIXME: we say 'c' instead of 'b', because of the propagation. (rdar://161360537) - if cond { return b } // expected-error {{independent copy of 'c' is required}} - return c // expected-error {{independent copy of 'c' is required}} + if cond { return b } // expected-warning {{independent copy of 'c' is required}} + return c // expected-warning {{independent copy of 'c' is required}} } -@_manualOwnership func renamed_return_fix1(_ cond: Bool, _ a: Triangle) -> Triangle { let b = copy a let c = copy b // FIXME: not needed! Is explicit_copy_value is blocking propagation? (rdar://161359163) @@ -113,8 +99,7 @@ func renamed_return_fix1(_ cond: Bool, _ a: Triangle) -> Triangle { } // FIXME: this crashes CopyPropagation! (rdar://161360764) -//@_manualOwnership -//func renamed_return_fix2(_ cond: Bool, _ a: Triangle) -> Triangle { +////func renamed_return_fix2(_ cond: Bool, _ a: Triangle) -> Triangle { // let b = a // let c = b // if cond { return copy b } @@ -123,20 +108,17 @@ func renamed_return_fix1(_ cond: Bool, _ a: Triangle) -> Triangle { /// MARK: method calls -@_manualOwnership func basic_methods_borrowing(_ t1: Triangle) { let t2 = Triangle() t1.borrowing() t2.borrowing() } -@_manualOwnership func basic_methods_consuming(_ t1: Triangle) { let t2 = Triangle() - t1.consuming() // expected-error {{independent copy of 't1' is required}} + t1.consuming() // expected-warning {{independent copy of 't1' is required}} t2.consuming() } -@_manualOwnership func basic_methods_consuming_fixed(_ t1: Triangle) { let t2 = Triangle() @@ -147,34 +129,28 @@ func basic_methods_consuming_fixed(_ t1: Triangle) { open class OpenClass { open func classMethod() {} } -@_manualOwnership func callOpenMethod(_ c: OpenClass) { return c.classMethod() } -@_manualOwnership @discardableResult func consumingFunc(_ t0: consuming Triangle) -> Bool { return false } -@_manualOwnership func plainFunc(_ t0: Triangle) {} -@_manualOwnership func basic_function_call(_ t1: Triangle) { - consumingFunc(t1) // expected-error {{independent copy of 't1' is required}} + consumingFunc(t1) // expected-warning {{independent copy of 't1' is required}} consumingFunc(copy t1) plainFunc(t1) } /// MARK: control-flow -@_manualOwnership func check_vars(_ t: Triangle, _ b: Bool) -> Triangle { var x = Triangle() - if b { x = t } // expected-error {{independent copy of 't' is required}} - return x // expected-error {{independent copy of 'x' is required}} + if b { x = t } // expected-warning {{independent copy of 't' is required}} + return x // expected-warning {{independent copy of 'x' is required}} } -@_manualOwnership func check_vars_fixed(_ t: Triangle, _ b: Bool) -> Triangle { var x = Triangle() if b { x = copy t } @@ -184,53 +160,45 @@ func check_vars_fixed(_ t: Triangle, _ b: Bool) -> Triangle { // FIXME: var's still have some issues // (1) MandatoryRedundantLoadElimination introduces a 'copy_value' in place of a 'load [copy]' (rdar://161359163) -@_manualOwnership func reassignments_0() -> Triangle { var t3 = Triangle() t3 = Triangle() - return t3 // expected-error {{independent copy of 't3' is required}} + return t3 // expected-warning {{independent copy of 't3' is required}} } -@_manualOwnership func reassignments_0_fixed_1() -> Triangle { var t3 = Triangle() t3 = Triangle() return copy t3 } -@_manualOwnership func reassignments_0_fixed_2() -> Triangle { var t3 = Triangle() t3 = Triangle() return consume t3 } -@_manualOwnership func reassignments_1() { var t3 = Triangle() t3 = Triangle() - t3.borrowing() // expected-error {{accessing 't3' may produce a copy; write 'copy' to acknowledge or 'consume' to elide}} + t3.borrowing() // expected-warning {{accessing 't3' may produce a copy; write 'copy' to acknowledge or 'consume' to elide}} } -@_manualOwnership func reassignments_1_fixed_1() { var t3 = Triangle() t3 = Triangle() (copy t3).borrowing() } -@_manualOwnership func reassignments_1_fixed_2() { var t3 = Triangle() t3 = Triangle() (consume t3).borrowing() } -@_manualOwnership public func basic_loop_trivial_values(_ t: Triangle, _ xs: [Triangle]) { var p: Pair = t.a - for x in xs { // expected-error {{independent copy of 'xs' is required}} + for x in xs { // expected-warning {{independent copy of 'xs' is required}} p = p.midpoint(x.a) } t.a = p } -@_manualOwnership public func basic_loop_trivial_values_fixed(_ t: Triangle, _ xs: [Triangle]) { var p: Pair = t.a for x in copy xs { @@ -245,16 +213,14 @@ public func basic_loop_trivial_values_fixed(_ t: Triangle, _ xs: [Triangle]) { // There's complexity in auto-generating a read accessor for classes, but if it's provided // we could then allow someone to elide the copy with a `borrow x` expression. -@_manualOwnership public func basic_loop_nontrivial_values(_ t: Triangle, _ xs: [Triangle]) { - var p: Pair = t.nontrivial.a // expected-error {{accessing 't.nontrivial' may produce a copy}} - for x in xs { // expected-error {{independent copy of 'xs' is required}} - p = p.midpoint(x.nontrivial.a) // expected-error {{accessing 'x.nontrivial' may produce a copy}} + var p: Pair = t.nontrivial.a // expected-warning {{accessing 't.nontrivial' may produce a copy}} + for x in xs { // expected-warning {{independent copy of 'xs' is required}} + p = p.midpoint(x.nontrivial.a) // expected-warning {{accessing 'x.nontrivial' may produce a copy}} } - t.nontrivial.a = p // expected-error {{accessing 't.nontrivial' may produce a copy}} + t.nontrivial.a = p // expected-warning {{accessing 't.nontrivial' may produce a copy}} } -@_manualOwnership public func basic_loop_nontrivial_values_fixed(_ t: Triangle, _ xs: [Triangle]) { var p: Pair = (copy t.nontrivial).a for x in copy xs { @@ -263,18 +229,16 @@ public func basic_loop_nontrivial_values_fixed(_ t: Triangle, _ xs: [Triangle]) (copy t.nontrivial).a = p } -@_manualOwnership public func basic_loop_nontrivial_values_reduced_copies(_ t: Triangle, _ xs: [Triangle]) { // FIXME: confusing variable names are chosen (rdar://161360537) - let nt = t.nontrivial // expected-error {{accessing 'nt' may produce a copy}} + let nt = t.nontrivial // expected-warning {{accessing 'nt' may produce a copy}} var p: Pair = nt.a for x in copy xs { - let xnt = x.nontrivial // expected-error {{accessing 'xnt' may produce a copy}} + let xnt = x.nontrivial // expected-warning {{accessing 'xnt' may produce a copy}} p = p.midpoint(xnt.a) } nt.a = p } -@_manualOwnership public func basic_loop_nontrivial_values_reduced_copies_fixed(_ t: Triangle, _ xs: [Triangle]) { let nt = copy t.nontrivial var p: Pair = nt.a @@ -291,96 +255,82 @@ let ref_result = [5, 13, 29] // FIXME: if we had a borrow operator, we could allow people to elide these simple copies that // are present to avoid exclusivity issues. We'd need to start generating read coroutines. -@_manualOwnership func access_global_1() -> Int { - return ref_result[2] // expected-error {{accessing 'ref_result' may produce a copy}} + return ref_result[2] // expected-warning {{accessing 'ref_result' may produce a copy}} } -@_manualOwnership func access_global_1_fixed() -> Int { return (copy ref_result)[2] } /// MARK: closures -@_manualOwnership func closure_basic(_ t: Triangle) -> () -> Triangle { - return { // expected-error {{closure capture of 't' requires independent copy of it; write [t = copy t]}} - return t // expected-error {{independent copy of 't' is required}} + return { // expected-warning {{closure capture of 't' requires independent copy of it; write [t = copy t]}} + return t // expected-warning {{independent copy of 't' is required}} } } -@_manualOwnership func closure_basic_almost_fixed_1(_ t: Triangle) -> () -> Triangle { // FIXME: Closure capture lists need to support the short-hand [copy t] that makes the // closure capture parameter @owned, rather than @guaranteed. Only can work for Copyable types! return { [x = copy t] in - return x // expected-error {{independent copy of 'x' is required}} + return x // expected-warning {{independent copy of 'x' is required}} } } -@_manualOwnership func closure_basic_almost_fixed_2(_ x: Triangle) -> () -> Triangle { - return { // expected-error {{closure capture of 'x' requires independent copy of it; write [x = copy x]}} + return { // expected-warning {{closure capture of 'x' requires independent copy of it; write [x = copy x]}} return copy x } } -@_manualOwnership func closure_basic_fixed(_ t: Triangle) -> () -> Triangle { return { [x = copy t] in return copy x } } -@_manualOwnership func closure_copies_in_body(_ t: Triangle) -> () -> Triangle { return { [x = copy t] in - eat(x) // expected-error {{independent copy of 'x' is required}} + eat(x) // expected-warning {{independent copy of 'x' is required}} use(x) - eat(x) // expected-error {{independent copy of 'x' is required}} - return x // expected-error {{independent copy of 'x' is required}} + eat(x) // expected-warning {{independent copy of 'x' is required}} + return x // expected-warning {{independent copy of 'x' is required}} } } -@_manualOwnership func closure_copies_in_body_noescape(_ t: Triangle) -> Triangle { let f = { [x = copy t] in - eat(x) // expected-error {{independent copy of 'x' is required}} + eat(x) // expected-warning {{independent copy of 'x' is required}} use(x) - eat(x) // expected-error {{independent copy of 'x' is required}} - return x // expected-error {{independent copy of 'x' is required}} + eat(x) // expected-warning {{independent copy of 'x' is required}} + return x // expected-warning {{independent copy of 'x' is required}} } return f() } -@_manualOwnership func simple_assert(_ f: @autoclosure () -> Bool) { guard f() else { fatalError() } } -@_manualOwnership func try_to_assert(_ n: Int, _ names: [String]) { simple_assert(names.count == n) } -@_manualOwnership func copy_in_autoclosure(_ t: Triangle) { - simple_assert(consumingFunc(t)) // expected-error {{independent copy of 't' is required}} + simple_assert(consumingFunc(t)) // expected-warning {{independent copy of 't' is required}} } -@_manualOwnership func copy_in_autoclosure_fixed(_ t: Triangle) { simple_assert(consumingFunc(copy t)) } -@_manualOwnership func nested_closures(_ t: Triangle) -> () -> (() -> Triangle) { - return { // expected-error {{closure capture of 't' requires independent copy of it; write [t = copy t]}} - { eat(t) }() // expected-error {{independent copy of 't' is required}} - return { // expected-error {{closure capture of 't' requires independent copy of it; write [t = copy t]}} - simple_assert(consumingFunc(t)) // expected-error {{independent copy of 't' is required}} - return t // expected-error {{independent copy of 't' is required}} + return { // expected-warning {{closure capture of 't' requires independent copy of it; write [t = copy t]}} + { eat(t) }() // expected-warning {{independent copy of 't' is required}} + return { // expected-warning {{closure capture of 't' requires independent copy of it; write [t = copy t]}} + simple_assert(consumingFunc(t)) // expected-warning {{independent copy of 't' is required}} + return t // expected-warning {{independent copy of 't' is required}} } } } -@_manualOwnership func nested_closures_fixed(_ t: Triangle) -> () -> (() -> Triangle) { return { [a = copy t] in { eat(copy a) }() @@ -390,28 +340,34 @@ func nested_closures_fixed(_ t: Triangle) -> () -> (() -> Triangle) { } } } +@_noManualOwnership +func nested_closures_DISABLED(_ t: Triangle) -> () -> (() -> Triangle) { + return { + { eat(t) }() + return { + simple_assert(consumingFunc(t)) + return t + } + } +} /// MARK: generics -@_manualOwnership func return_generic(_ t: T) -> T { - return t // expected-error {{accessing 't' may produce a copy}} + return t // expected-warning {{accessing 't' may produce a copy}} } -@_manualOwnership func return_generic_fixed(_ t: T) -> T { return copy t } -@_manualOwnership func reassign_with_lets(_ t: T) -> T { - let x = t // expected-error {{accessing 't' may produce a copy}} - let y = x // expected-error {{accessing 'x' may produce a copy}} - let z = y // expected-error {{accessing 'y' may produce a copy}} + let x = t // expected-warning {{accessing 't' may produce a copy}} + let y = x // expected-warning {{accessing 'x' may produce a copy}} + let z = y // expected-warning {{accessing 'y' may produce a copy}} return copy z } // FIXME: copy propagation has no effect on address-only types, so this is quite verbose. -@_manualOwnership func reassign_with_lets_fixed(_ t: T) -> T { let x = copy t let y = copy x @@ -419,31 +375,34 @@ func reassign_with_lets_fixed(_ t: T) -> T { return copy z } -@_manualOwnership func copy_generic(_ t: T) { - consume_generic(t) // expected-error {{accessing 't' may produce a copy}} + consume_generic(t) // expected-warning {{accessing 't' may produce a copy}} + borrow_generic(t) + consume_generic(t) // expected-warning {{accessing 't' may produce a copy}} +} + +@_noManualOwnership +func copy_generic_DISABLED(_ t: T) { + consume_generic(t) borrow_generic(t) - consume_generic(t) // expected-error {{accessing 't' may produce a copy}} + consume_generic(t) } -@_manualOwnership func copy_generic_fixed(_ t: T) { consume_generic(copy t) borrow_generic(t) consume_generic(copy t) } -@_manualOwnership func benchCaptureProp( _ s: S, _ f: (S.Element, S.Element) -> S.Element) -> S.Element { - var it = s.makeIterator() // expected-error {{accessing 's' may produce a copy}} + var it = s.makeIterator() // expected-warning {{accessing 's' may produce a copy}} let initial = it.next()! return - IteratorSequence(it) // expected-error {{accessing 'it' may produce a copy}} + IteratorSequence(it) // expected-warning {{accessing 'it' may produce a copy}} .reduce(initial, f) } -@_manualOwnership func benchCaptureProp_fixed( _ s: S, _ f: (S.Element, S.Element) -> S.Element) -> S.Element { @@ -455,12 +414,10 @@ func benchCaptureProp_fixed( } extension FixedWidthInteger { - @_manualOwnership func leftRotate(_ distance: Int) -> Self { return (self << distance) | (self >> (Self.bitWidth - distance)) } - @_manualOwnership mutating func rotatedLeft(_ distance: Int) { self = (copy self).leftRotate(distance) } @@ -469,11 +426,10 @@ extension FixedWidthInteger { struct CollectionOf32BitLittleEndianIntegers where BaseCollection.Element == UInt8 { var baseCollection: BaseCollection - @_manualOwnership init(_ baseCollection: BaseCollection) { precondition(baseCollection.count % 4 == 0) - self.baseCollection = baseCollection // expected-error {{accessing 'baseCollection' may produce a copy}} - } // expected-error {{accessing 'self' may produce a copy}} + self.baseCollection = baseCollection // expected-warning {{accessing 'baseCollection' may produce a copy}} + } // expected-warning {{accessing 'self' may produce a copy}} // FIXME: the above initializer shouldn't have any diagnostics } diff --git a/test/SIL/manual_ownership_exclusivity.swift b/test/SIL/manual_ownership_exclusivity.swift new file mode 100644 index 0000000000000..c46152ab8b786 --- /dev/null +++ b/test/SIL/manual_ownership_exclusivity.swift @@ -0,0 +1,71 @@ +// RUN: %target-swift-frontend %s -emit-sil -verify \ +// RUN: -enable-experimental-feature ManualOwnership \ +// RUN: -Wwarning DynamicExclusivity + +// REQUIRES: swift_feature_ManualOwnership + +protocol NutritionInfo { + var calories: Int { get } +} + +public class Food: NutritionInfo { + var calories = 0 +} + +public class Donut : Food { + + let next: Donut? = nil + + override init() { + super.init() + self.calories = 100 // expected-warning {{exclusive access here will be checked at runtime}} + } + + convenience init(calories c: Int) { + self.init() + self.calories = c // expected-warning {{exclusive access here will be checked at runtime}} + } +} + +extension Int { func greaterThanZero() -> Bool { self > 0 } } + +var expectedCalories: Array = [120, 203, 1502] + +func accessGlobal_map() -> Array { + return expectedCalories.map(Donut.init(calories:)) // expected-warning {{accessing 'expectedCalories' here may incur runtime exclusivity check, because it involves a global variable}} +} + +func accessGlobal_member() -> Int { + return expectedCalories.count // expected-warning {{accessing 'expectedCalories' here may incur runtime exclusivity check, because it involves a global variable}} +} + +@_noManualOwnership +func accessGlobal_member_DISABLED() -> Int { + return expectedCalories.count +} + +var globalDonut: Donut = Donut() + +func accessGlobalClass() { + let x = globalDonut.calories // expected-warning {{accessing 'globalDonut' here may incur runtime exclusivity check, because it involves a global variable}} + // expected-warning@-1 {{exclusive access here will be checked at runtime}} + + expectedCalories.append(x) // expected-warning {{accessing 'expectedCalories' here may incur runtime exclusivity check, because it involves a global variable}} +} + +func accessClassSimple(_ d2: Donut) -> Int { + let d1 = Donut() + return d1.calories // expected-warning {{exclusive access here will be checked at runtime}} + + d2.calories // expected-warning {{exclusive access here will be checked at runtime}} +} + +func accessClassParam_chain(_ donut: Donut) -> Int { + return donut.next?.next?.calories ?? Int.max + // expected-warning@-1 {{exclusive access here will be checked at runtime}} +} + +func accessClassLocal_chain() -> Int { + let donut = Donut() + return donut.next?.next?.calories ?? Int.max + // expected-warning@-1 {{exclusive access here will be checked at runtime}} +} diff --git a/test/SILGen/manual_ownership.swift b/test/SILGen/manual_ownership.swift index b18403eca9813..b28dc2f076c78 100644 --- a/test/SILGen/manual_ownership.swift +++ b/test/SILGen/manual_ownership.swift @@ -19,7 +19,6 @@ class TriangleClass { // CHECK-NEXT: destroy_value [[SHAPE]] // CHECK-NEXT: return [[COPY]] // CHECK-NEXT: } // end sil function 'basic_access_of_loadable' -@_manualOwnership @_silgen_name("basic_access_of_loadable") func basic_access_of_loadable(_ t: TriangleClass) -> ShapeClass { return copy t.shape @@ -41,7 +40,6 @@ func basic_access_of_loadable(_ t: TriangleClass) -> ShapeClass { // CHECK-NEXT: end_access [[ADDR2]] // CHECK: return [[X_COPY]] // CHECK-NEXT: } // end sil function 'var_assign' -@_manualOwnership @_silgen_name("var_assign") func var_assign(_ t: TriangleClass, _ b: Bool) -> TriangleClass { var x = TriangleClass() @@ -55,7 +53,6 @@ func var_assign(_ t: TriangleClass, _ b: Bool) -> TriangleClass { // CHECK-NEXT: [[IMPL_COPY:%.*]] = copy_value %0 // CHECK-NEXT: return [[IMPL_COPY]] // CHECK-NEXT: } // end sil function 'return_borrowed' -@_manualOwnership @_silgen_name("return_borrowed") func return_borrowed(_ t: borrowing TriangleClass) -> TriangleClass { return t @@ -74,7 +71,6 @@ func return_borrowed(_ t: borrowing TriangleClass) -> TriangleClass { // CHECK-NEXT: destroy_value // CHECK-NEXT: return [[IMPL_COPY]] // CHECK-NEXT: } // end sil function 'return_consumingParam' -@_manualOwnership @_silgen_name("return_consumingParam") func return_consumingParam(_ t: consuming TriangleClass) -> TriangleClass { return t @@ -89,7 +85,6 @@ func return_consumingParam(_ t: consuming TriangleClass) -> TriangleClass { // CHECK-NEXT: destroy_value %0 // CHECK-NEXT: return [[IMPL_COPY]] // CHECK-NEXT: } // end sil function 'return_owned' -@_manualOwnership @_silgen_name("return_owned") func return_owned(_ t: __owned TriangleClass) -> TriangleClass { return t diff --git a/test/Sema/manualownership_attr.swift b/test/Sema/manualownership_attr.swift index be99aeb1ccd09..964be9133a9c7 100644 --- a/test/Sema/manualownership_attr.swift +++ b/test/Sema/manualownership_attr.swift @@ -7,7 +7,6 @@ class C {} -@_manualOwnership func hello() -> (C, C) { @_noImplicitCopy let x = C() // expected-error {{'@_noImplicitCopy' cannot be used with ManualOwnership}} return (x, x) diff --git a/userdocs/diagnostics/dynamic-exclusivity.md b/userdocs/diagnostics/dynamic-exclusivity.md new file mode 100644 index 0000000000000..86ef66ad23cf6 --- /dev/null +++ b/userdocs/diagnostics/dynamic-exclusivity.md @@ -0,0 +1,3 @@ +# Dynamic Exclusivity (Experimental Diagnostics) + +TODO explain diff --git a/userdocs/diagnostics/semantic-copies.md b/userdocs/diagnostics/semantic-copies.md new file mode 100644 index 0000000000000..3d415ecb75fcc --- /dev/null +++ b/userdocs/diagnostics/semantic-copies.md @@ -0,0 +1,3 @@ +# Semantic Copies (Experimental Diagnostics) + +TODO explain