diff --git a/lib/SILOptimizer/Mandatory/TransferNonSendable.cpp b/lib/SILOptimizer/Mandatory/TransferNonSendable.cpp index c81a7f319343f..d84e8d8ba2e54 100644 --- a/lib/SILOptimizer/Mandatory/TransferNonSendable.cpp +++ b/lib/SILOptimizer/Mandatory/TransferNonSendable.cpp @@ -145,12 +145,30 @@ struct UseDefChainVisitor case ProjectionKind::Tuple: { // These are merges if we have multiple fields. auto *tti = cast(inst); + + // See if our result type is a sendable type. In such a case, we do not + // want to look through the tuple_element_addr since we do not want to + // identify the sendable type with the non-sendable operand. These we + // are always going to ignore anyways since a sendable let/var field of + // a struct can always be used. + if (!isNonSendableType(tti->getType(), tti->getFunction())) + return SILValue(); + isMerge |= tti->getOperand()->getType().getNumTupleElements() > 1; break; } case ProjectionKind::Struct: - // These are merges if we have multiple fields. auto *sea = cast(inst); + + // See if our result type is a sendable type. In such a case, we do not + // want to look through the struct_element_addr since we do not want to + // identify the sendable type with the non-sendable operand. These we + // are always going to ignore anyways since a sendable let/var field of + // a struct can always be used. + if (!isNonSendableType(sea->getType(), sea->getFunction())) + return SILValue(); + + // These are merges if we have multiple fields. isMerge |= sea->getOperand()->getType().getNumNominalFields() > 1; break; } @@ -1147,13 +1165,21 @@ class PartitionOpTranslator { case SILInstructionKind::EndInitLetRefInst: case SILInstructionKind::InitEnumDataAddrInst: case SILInstructionKind::OpenExistentialAddrInst: - case SILInstructionKind::StructElementAddrInst: - case SILInstructionKind::TupleElementAddrInst: case SILInstructionKind::UncheckedRefCastInst: case SILInstructionKind::UncheckedTakeEnumDataAddrInst: case SILInstructionKind::UpcastInst: return translateSILLookThrough(inst->getResult(0), inst->getOperand(0)); + case SILInstructionKind::TupleElementAddrInst: + case SILInstructionKind::StructElementAddrInst: { + auto *svi = cast(inst); + // If we have a sendable field... we can always access it after + // transferring... so do not track this. + if (!isNonSendableType(svi->getType())) + return; + return translateSILLookThrough(svi->getResult(0), svi->getOperand(0)); + } + // We identify tuple results with their operand's id. case SILInstructionKind::DestructureTupleInst: case SILInstructionKind::DestructureStructInst: @@ -1183,7 +1209,6 @@ class PartitionOpTranslator { case SILInstructionKind::PointerToAddressInst: case SILInstructionKind::ProjectBlockStorageInst: case SILInstructionKind::RefToUnmanagedInst: - case SILInstructionKind::StructExtractInst: case SILInstructionKind::TailAddrInst: case SILInstructionKind::ThickToObjCMetatypeInst: case SILInstructionKind::ThinToThickFunctionInst: @@ -1191,6 +1216,7 @@ class PartitionOpTranslator { case SILInstructionKind::UncheckedEnumDataInst: case SILInstructionKind::UncheckedOwnershipConversionInst: case SILInstructionKind::UnmanagedToRefInst: + return translateSILAssign(inst); // RefElementAddrInst is not considered to be a lookThrough since we want to // consider the address projected from the class to be a separate value that @@ -1198,8 +1224,29 @@ class PartitionOpTranslator { // do this is to ensure that if we assign into the ref_element_addr memory, // we do not consider writes into the struct that contains the // ref_element_addr to be merged into. - case SILInstructionKind::RefElementAddrInst: + case SILInstructionKind::RefElementAddrInst: { + auto *reai = cast(inst); + // If we are accessing a let of a Sendable type, do not treat the + // ref_element_addr as a require use. + if (reai->getField()->isLet() && !isNonSendableType(reai->getType())) { + LLVM_DEBUG(llvm::dbgs() << " Found a let! Not tracking!\n"); + return; + } + return translateSILAssign(inst); + } + + case SILInstructionKind::TupleExtractInst: + case SILInstructionKind::StructExtractInst: { + auto *svi = cast(inst); + // If our result is a Sendable type regardless of if it is a let or a var, + // we do not need to track it. + if (!isNonSendableType(svi->getType())) { + LLVM_DEBUG(llvm::dbgs() + << " Found a sendable field... Not Tracking!\n"); + return; + } return translateSILAssign(inst); + } /// Enum inst is handled specially since if it does not have an argument, /// we must assign fresh. Otherwise, we must propagate. diff --git a/test/Concurrency/sendnonsendable_basic.swift b/test/Concurrency/sendnonsendable_basic.swift index 6cb365879c0c7..f7cca9ec793b3 100644 --- a/test/Concurrency/sendnonsendable_basic.swift +++ b/test/Concurrency/sendnonsendable_basic.swift @@ -15,12 +15,14 @@ //////////////////////// /// Classes are always non-sendable, so this is non-sendable -class NonSendableKlass { // expected-complete-note 16{{}} +class NonSendableKlass { // expected-complete-note 22{{}} var field: NonSendableKlass? = nil func asyncCall() async {} } +class SendableKlass : @unchecked Sendable {} + actor Actor { var klass = NonSendableKlass() // expected-typechecker-only-note @-1 7{{property declared here}} @@ -622,3 +624,308 @@ func multipleFieldTupleMergeTest2() async { useValue(box.1) useValue(box) } + +/////////////////////////// +// MARK: ClassFieldTests // +/////////////////////////// + +class ClassFieldTests { // expected-complete-note 6{{}} + let letSendableTrivial = 0 + let letSendableNonTrivial = SendableKlass() + let letNonSendableNonTrivial = NonSendableKlass() + var varSendableTrivial = 0 + var varSendableNonTrivial = SendableKlass() + var varNonSendableNonTrivial = NonSendableKlass() +} + +func letSendableTrivialClassFieldTest() async { + let test = ClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'ClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'ClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letSendableTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func letSendableNonTrivialClassFieldTest() async { + let test = ClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'ClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'ClassFieldTests' into main actor-isolated context may introduce data races}} + + _ = test.letSendableNonTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func letNonSendableNonTrivialClassFieldTest() async { + let test = ClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'ClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'ClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letNonSendableNonTrivial // expected-tns-note {{access here could race}} + useValue(test) +} + +func varSendableTrivialClassFieldTest() async { + let test = ClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'ClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'ClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varSendableTrivial // expected-tns-note {{access here could race}} + useValue(test) +} + +func varSendableNonTrivialClassFieldTest() async { + let test = ClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'ClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'ClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varSendableNonTrivial // expected-tns-note {{access here could race}} + useValue(test) +} + +func varNonSendableNonTrivialClassFieldTest() async { + let test = ClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'ClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'ClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varNonSendableNonTrivial // expected-tns-note {{access here could race}} + useValue(test) +} + +//////////////////////////////// +// MARK: FinalClassFieldTests // +//////////////////////////////// + +final class FinalClassFieldTests { // expected-complete-note 6 {{}} + let letSendableTrivial = 0 + let letSendableNonTrivial = SendableKlass() + let letNonSendableNonTrivial = NonSendableKlass() + var varSendableTrivial = 0 + var varSendableNonTrivial = SendableKlass() + var varNonSendableNonTrivial = NonSendableKlass() +} + +func letSendableTrivialFinalClassFieldTest() async { + let test = FinalClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'FinalClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'FinalClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letSendableTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func letSendableNonTrivialFinalClassFieldTest() async { + let test = FinalClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'FinalClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'FinalClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letSendableNonTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func letNonSendableNonTrivialFinalClassFieldTest() async { + let test = FinalClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'FinalClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'FinalClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letNonSendableNonTrivial // expected-tns-note {{access here could race}} + useValue(test) +} + +func varSendableTrivialFinalClassFieldTest() async { + let test = FinalClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'FinalClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'FinalClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varSendableTrivial // expected-tns-note {{access here could race}} + useValue(test) +} + +func varSendableNonTrivialFinalClassFieldTest() async { + let test = FinalClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'FinalClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'FinalClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varSendableNonTrivial // expected-tns-note {{access here could race}} + useValue(test) +} + +func varNonSendableNonTrivialFinalClassFieldTest() async { + let test = FinalClassFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'FinalClassFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'FinalClassFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varNonSendableNonTrivial // expected-tns-note {{access here could race}} + useValue(test) +} + +//////////////////////////// +// MARK: StructFieldTests // +//////////////////////////// + +struct StructFieldTests { // expected-complete-note 12 {{}} + let letSendableTrivial = 0 + let letSendableNonTrivial = SendableKlass() + let letNonSendableNonTrivial = NonSendableKlass() + var varSendableTrivial = 0 + var varSendableNonTrivial = SendableKlass() + var varNonSendableNonTrivial = NonSendableKlass() +} + +func letSendableTrivialLetStructFieldTest() async { + let test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letSendableTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func letSendableNonTrivialLetStructFieldTest() async { + let test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letSendableNonTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func letNonSendableNonTrivialLetStructFieldTest() async { + let test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + let z = test.letNonSendableNonTrivial // expected-tns-note {{access here could race}} + _ = z + useValue(test) +} + +func varSendableTrivialLetStructFieldTest() async { + let test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varSendableTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func varSendableNonTrivialLetStructFieldTest() async { + let test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varSendableNonTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func varNonSendableNonTrivialLetStructFieldTest() async { + let test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + let z = test.varNonSendableNonTrivial // expected-tns-note {{access here could race}} + _ = z + useValue(test) +} + +func letSendableTrivialVarStructFieldTest() async { + var test = StructFieldTests() + test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letSendableTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func letSendableNonTrivialVarStructFieldTest() async { + var test = StructFieldTests() + test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + _ = test.letSendableNonTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func letNonSendableNonTrivialVarStructFieldTest() async { + var test = StructFieldTests() + test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + let z = test.letNonSendableNonTrivial // expected-tns-note {{access here could race}} + _ = z + useValue(test) +} + +func varSendableTrivialVarStructFieldTest() async { + var test = StructFieldTests() + test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varSendableTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func varSendableNonTrivialVarStructFieldTest() async { + var test = StructFieldTests() + test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + _ = test.varSendableNonTrivial + useValue(test) // expected-tns-note {{access here could race}} +} + +func varNonSendableNonTrivialVarStructFieldTest() async { + var test = StructFieldTests() + test = StructFieldTests() + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type 'StructFieldTests' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} + let z = test.varNonSendableNonTrivial // expected-tns-note {{access here could race}} + _ = z + useValue(test) +} + +//////////////////////////// +// MARK: TupleFieldTests // +//////////////////////////// + +func varSendableTrivialLetTupleFieldTest() async { + let test = (0, SendableKlass(), NonSendableKlass()) + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' into main actor-isolated context may introduce data races}} + let z = test.0 + useValue(z) + useValue(test) // expected-tns-note {{access here could race}} +} + +func varSendableNonTrivialLetTupleFieldTest() async { + let test = (0, SendableKlass(), NonSendableKlass()) + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' into main actor-isolated context may introduce data races}} + let z = test.1 + useValue(z) + useValue(test) // expected-tns-note {{access here could race}} +} + +func varNonSendableNonTrivialLetTupleFieldTest() async { + let test = (0, SendableKlass(), NonSendableKlass()) + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' into main actor-isolated context may introduce data races}} + let z = test.2 + // The SIL emitted for the assignment of the tuple is just a jumble of + // instructions that are not semantically significant. The result is that we + // need a useValue here to actual provide something for the pass to chew on. + useValue(z) // expected-tns-note {{access here could race}} + useValue(test) +} + +func varSendableTrivialVarTupleFieldTest() async { + var test = (0, SendableKlass(), NonSendableKlass()) + test = (0, SendableKlass(), NonSendableKlass()) + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' into main actor-isolated context may introduce data races}} + _ = test.0 + useValue(test) // expected-tns-note {{access here could race}} +} + +func varSendableNonTrivialVarTupleFieldTest() async { + var test = (0, SendableKlass(), NonSendableKlass()) + test = (0, SendableKlass(), NonSendableKlass()) + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' into main actor-isolated context may introduce data races}} + _ = test.1 + useValue(test) // expected-tns-note {{access here could race}} +} + +func varNonSendableNonTrivialVarTupleFieldTest() async { + var test = (0, SendableKlass(), NonSendableKlass()) + test = (0, SendableKlass(), NonSendableKlass()) + await transferToMain(test) // expected-tns-warning {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}} + // expected-complete-warning @-1 {{passing argument of non-sendable type '(Int, SendableKlass, NonSendableKlass)' into main actor-isolated context may introduce data races}} + let z = test.2 // expected-tns-note {{access here could race}} + useValue(z) + useValue(test) +} +