From afe811764bf5663b7b2ef58d098e5d3cc2185e71 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 17 Dec 2024 18:15:49 -0800 Subject: [PATCH 1/7] [sil] Add a new instruction ignored_use. This is used for synthetic uses like _ = x that do not act as a true use but instead only suppress unused variable warnings. This patch just adds the instruction. Eventually, we can use it to move the unused variable warning from Sema to SIL slimmming the type checker down a little bit... but for now I am using it so that other diagnostic passes can have a SIL instruction (with SIL location) so that we can emit diagnostics on code like _ = x. Today we just do not emit anything at all for that case so a diagnostic SIL pass would not see any instruction that it could emit a diagnostic upon. In the next patch of this series, I am going to add SILGen support to do that. (cherry picked from commit 7ae56aab2efe12785b37009f9763d2037e9aa8e5) --- SwiftCompilerSources/Sources/SIL/Instruction.swift | 3 +++ SwiftCompilerSources/Sources/SIL/Registration.swift | 1 + include/swift/SIL/SILBuilder.h | 9 +++++++++ include/swift/SIL/SILCloner.h | 8 ++++++++ include/swift/SIL/SILInstruction.h | 11 +++++++++++ include/swift/SIL/SILNodes.def | 3 +++ lib/IRGen/IRGenSIL.cpp | 3 +++ lib/SIL/IR/OperandOwnership.cpp | 1 + lib/SIL/IR/SILPrinter.cpp | 4 ++++ lib/SIL/Parser/ParseSIL.cpp | 5 +++++ lib/SIL/Utils/InstructionUtils.cpp | 4 +++- lib/SILOptimizer/Analysis/RegionAnalysis.cpp | 1 + lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp | 1 + lib/SILOptimizer/Utils/SILInliner.cpp | 1 + lib/Serialization/DeserializeSIL.cpp | 8 ++++++++ lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SerializeSIL.cpp | 11 +++++++++++ test/SIL/Parser/basic2.sil | 12 +++++++++++- test/SIL/Serialization/basic2.sil | 11 +++++++++++ 19 files changed, 96 insertions(+), 3 deletions(-) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index a21ebf77628d9..6a51f0d0e09b5 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -1615,3 +1615,6 @@ final public class ThunkInst : Instruction { final public class MergeIsolationRegionInst : Instruction { } + +final public class IgnoredUseInst : Instruction { +} diff --git a/SwiftCompilerSources/Sources/SIL/Registration.swift b/SwiftCompilerSources/Sources/SIL/Registration.swift index ddba09010419f..041aba2fbaae3 100644 --- a/SwiftCompilerSources/Sources/SIL/Registration.swift +++ b/SwiftCompilerSources/Sources/SIL/Registration.swift @@ -258,4 +258,5 @@ public func registerSILClasses() { register(CheckedCastAddrBranchInst.self) register(ThunkInst.self) register(MergeIsolationRegionInst.self) + register(IgnoredUseInst.self) } diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 169c550bd5ee4..236fa3a21a939 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -3089,6 +3089,15 @@ class SILBuilder { getModule(), getSILDebugLocation(Loc), Decl)); } + //===--------------------------------------------------------------------===// + // Misc Uses + //===--------------------------------------------------------------------===// + + IgnoredUseInst *createIgnoredUse(SILLocation loc, SILValue value) { + return insert(new (getModule()) + IgnoredUseInst(getSILDebugLocation(loc), value)); + } + //===--------------------------------------------------------------------===// // Private Helper Methods //===--------------------------------------------------------------------===// diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 4abdbf774cea6..a7c54b173638b 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -3796,6 +3796,14 @@ void SILCloner::visitHasSymbolInst(HasSymbolInst *Inst) { Inst->getDecl())); } +template +void SILCloner::visitIgnoredUseInst(IgnoredUseInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction( + Inst, getBuilder().createIgnoredUse(getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()))); +} + } // end namespace swift #endif diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 6f06ec491109d..6b6af80f46fca 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -11685,6 +11685,17 @@ class MergeIsolationRegionInst final } }; +/// An instruction that represents a semantic-less use that is used to +/// suppresses unused value variable warnings. E.x.: _ = x. +class IgnoredUseInst final + : public UnaryInstructionBase { + friend SILBuilder; + + IgnoredUseInst(SILDebugLocation loc, SILValue operand) + : UnaryInstructionBase(loc, operand) {} +}; + inline SILType *AllocRefInstBase::getTypeStorage() { // If the size of the subclasses are equal, then all of this compiles away. if (auto I = dyn_cast(this)) diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 881f8a02b5e4c..7de4d3a590dae 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -897,6 +897,9 @@ NON_VALUE_INST(MarkUnresolvedMoveAddrInst, mark_unresolved_move_addr, NON_VALUE_INST(MergeIsolationRegionInst, merge_isolation_region, SILInstruction, None, DoesNotRelease) +NON_VALUE_INST(IgnoredUseInst, ignored_use, + SILInstruction, None, DoesNotRelease) + NON_VALUE_INST(IncrementProfilerCounterInst, increment_profiler_counter, SILInstruction, MayReadWrite, DoesNotRelease) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 5d48d165b2a33..141d2107229bb 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1273,6 +1273,9 @@ class IRGenSILFunction : visitMoveOnlyWrapperToCopyableBoxInst(MoveOnlyWrapperToCopyableBoxInst *i) { llvm_unreachable("OSSA instruction"); } + + void visitIgnoredUseInst(IgnoredUseInst *i) {} + void visitMoveOnlyWrapperToCopyableAddrInst(MoveOnlyWrapperToCopyableAddrInst *i) { auto e = getLoweredExplosion(i->getOperand()); diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 247935c46178b..890a126bf959b 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -214,6 +214,7 @@ OPERAND_OWNERSHIP(TrivialUse, GlobalAddr) // The dealloc_stack_ref operand needs to have NonUse ownership because // this use comes after the last consuming use (which is usually a dealloc_ref). OPERAND_OWNERSHIP(NonUse, DeallocStackRef) +OPERAND_OWNERSHIP(InstantaneousUse, IgnoredUse) // Use an owned or guaranteed value only for the duration of the operation. OPERAND_OWNERSHIP(InstantaneousUse, ExistentialMetatype) diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index a0d29d2692ab2..2420b35b48974 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2812,6 +2812,10 @@ class SILPrinter : public SILInstructionVisitor { *this << GI->getFormalResumeType(); } + void visitIgnoredUseInst(IgnoredUseInst *i) { + *this << getIDAndType(i->getOperand()); + } + void visitGetAsyncContinuationAddrInst(GetAsyncContinuationAddrInst *GI) { if (GI->throws()) *this << "[throws] "; diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 213134deecfda..00397705e5c5d 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -5068,6 +5068,11 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, } break; } + case SILInstructionKind::IgnoredUseInst: + if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createIgnoredUse(InstLoc, Val); + break; case SILInstructionKind::DeallocStackInst: if (parseTypedValueRef(Val, B) || parseSILDebugLocation(InstLoc, B)) diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 60c1af0df33f8..35452d7bec3e8 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -334,7 +334,8 @@ bool swift::isEndOfScopeMarker(SILInstruction *user) { bool swift::isIncidentalUse(SILInstruction *user) { return isEndOfScopeMarker(user) || user->isDebugInstruction() || - isa(user) || isa(user); + isa(user) || isa(user) || + isa(user); } bool swift::onlyAffectsRefCount(SILInstruction *user) { @@ -622,6 +623,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) case SILInstructionKind::DebugStepInst: case SILInstructionKind::FunctionExtractIsolationInst: case SILInstructionKind::TypeValueInst: + case SILInstructionKind::IgnoredUseInst: return RuntimeEffect::NoEffect; case SILInstructionKind::OpenExistentialMetatypeInst: diff --git a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp index d2cd28a7ced68..a95b13b4adff0 100644 --- a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp @@ -3053,6 +3053,7 @@ CONSTANT_TRANSLATION(DebugStepInst, Ignored) CONSTANT_TRANSLATION(IncrementProfilerCounterInst, Ignored) CONSTANT_TRANSLATION(SpecifyTestInst, Ignored) CONSTANT_TRANSLATION(TypeValueInst, Ignored) +CONSTANT_TRANSLATION(IgnoredUseInst, Ignored) //===--- // Require diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index b700e73048db3..6fe0f50ba86da 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -333,6 +333,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::PackElementSetInst: case SILInstructionKind::TuplePackElementAddrInst: case SILInstructionKind::TypeValueInst: + case SILInstructionKind::IgnoredUseInst: // Handle by operand and result check. break; diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index a0bb7b19d5790..da54b8e605890 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -900,6 +900,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::MoveOnlyWrapperToCopyableAddrInst: case SILInstructionKind::CopyableToMoveOnlyWrapperAddrInst: case SILInstructionKind::MoveOnlyWrapperToCopyableBoxInst: + case SILInstructionKind::IgnoredUseInst: return InlineCost::Free; // Typed GEPs are free. diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index c1bb682289583..6a7b230fd68ca 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -2196,6 +2196,14 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, getSILType(Ty, (SILValueCategory)TyCategory, Fn))); break; } + case SILInstructionKind::IgnoredUseInst: { + assert(RecordKind == SIL_ONE_OPERAND && "Should be one operand"); + auto Ty = MF->getType(TyID); + ResultInst = Builder.createIgnoredUse( + Loc, getLocalValue(Builder.maybeGetFunction(), ValID, + getSILType(Ty, (SILValueCategory)TyCategory, Fn))); + break; + } case SILInstructionKind::DeallocPackInst: { auto Ty = MF->getType(TyID); ResultInst = Builder.createDeallocPack( diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index e084414907f0d..8c6caf0ce649c 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 903; // @main attribute moved +const uint16_t SWIFTMODULE_VERSION_MINOR = 904; // ignored_use /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 3b4aeb329c4c3..153d3f88158ca 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1683,6 +1683,17 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { break; } + case SILInstructionKind::IgnoredUseInst: { + // Use SILOneOperandLayout to specify our operand. + auto *iui = cast(&SI); + unsigned abbrCode = SILAbbrCodes[SILOneOperandLayout::Code]; + SILOneOperandLayout::emitRecord( + Out, ScratchRecord, abbrCode, (unsigned)iui->getKind(), 0, + S.addTypeRef(iui->getOperand()->getType().getRawASTType()), + (unsigned)iui->getOperand()->getType().getCategory(), + addValueRef(iui->getOperand())); + break; + } case SILInstructionKind::DynamicFunctionRefInst: { // Use SILOneOperandLayout to specify the function type and the function // name (IdentifierID). diff --git a/test/SIL/Parser/basic2.sil b/test/SIL/Parser/basic2.sil index cb16dc322cc78..cbd77e5732b25 100644 --- a/test/SIL/Parser/basic2.sil +++ b/test/SIL/Parser/basic2.sil @@ -469,4 +469,14 @@ bb0(%0 : @guaranteed $Klass): dealloc_stack %1 : $*MoveOnlyPair %9999 = tuple () return %9999 : $() -} \ No newline at end of file +} + +// CHECK-LABEL: sil [ossa] @ignored_use_test : $@convention(thin) (@guaranteed Klass) -> () { +// CHECK: ignored_use %0 : $Klass +// CHECK: } // end sil function 'ignored_use_test' +sil [ossa] @ignored_use_test : $@convention(thin) (@guaranteed Klass) -> () { +bb0(%0 : @guaranteed $Klass): + ignored_use %0 : $Klass + %9999 = tuple () + return %9999 : $() +} diff --git a/test/SIL/Serialization/basic2.sil b/test/SIL/Serialization/basic2.sil index d5053af45dfa3..3061ed859a6f8 100644 --- a/test/SIL/Serialization/basic2.sil +++ b/test/SIL/Serialization/basic2.sil @@ -15,6 +15,16 @@ class Klass {} sil @getC : $@convention(thin) () -> (@owned C) sil @getKlass : $@convention(thin) () -> (@owned Klass) +// CHECK-LABEL: sil [ossa] @ignored_use_test : $@convention(thin) (@guaranteed Klass) -> () { +// CHECK: ignored_use %0 : $Klass +// CHECK: } // end sil function 'ignored_use_test' +sil [ossa] @ignored_use_test : $@convention(thin) (@guaranteed Klass) -> () { +bb0(%0 : @guaranteed $Klass): + ignored_use %0 : $Klass + %9999 = tuple () + return %9999 : $() +} + // CHECK-LABEL: sil [ossa] @merge_isolation_region : $@convention(thin) (@guaranteed Klass) -> () { // CHECK: merge_isolation_region %0 : $Klass, %1 : $*C // CHECK: merge_isolation_region %1 : $*C, %0 : $Klass, %0 : $Klass @@ -365,3 +375,4 @@ bb0(%0 : $*Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): %9999 = tuple () return %9999 : $() } + From 348931f69f091220b0bcdbe3ed154c4d1221cff0 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 9 Jan 2025 16:50:18 -0800 Subject: [PATCH 2/7] [silgen] Change SILGen to emit ignored_user for emitIgnoredExpr and black hole initialization. (cherry picked from commit 6058b1d9bda3869e7e32ad6394a9cacbcc7523bf) Conflicts: test/SILGen/sendable_to_any_for_generic_arguments.swift --- lib/SIL/Verifier/SILVerifier.cpp | 1 + lib/SILGen/SILGenDecl.cpp | 71 ++++++++++--------- lib/SILGen/SILGenExpr.cpp | 31 +++++++- .../Mandatory/DiagnoseUnreachable.cpp | 8 +++ .../Mandatory/LowerTupleAddrConstructor.cpp | 29 +++++--- .../MoveOnlyWrappedTypeEliminator.cpp | 1 + .../Mandatory/OwnershipModelEliminator.cpp | 5 ++ .../SILOptimizer/activity_analysis.swift | 2 +- test/Concurrency/transfernonsendable.swift | 8 +-- test/SILGen/class_bound_protocols.swift | 1 + test/SILGen/do_expr.swift | 1 + test/SILGen/enum.swift | 8 +++ test/SILGen/errors.swift | 7 ++ test/SILGen/expressions.swift | 1 + test/SILGen/extern_c.swift | 2 +- test/SILGen/indirect_enum.swift | 10 +++ test/SILGen/initializers.swift | 1 + test/SILGen/keypaths.swift | 14 ++-- test/SILGen/lifetime_unions.swift | 1 + test/SILGen/metatypes.swift | 1 + test/SILGen/optional.swift | 1 + test/SILGen/partial_apply_protocol.swift | 2 +- test/SILGen/properties.swift | 1 + test/SILGen/protocol_with_superclass.swift | 4 ++ ...rotocol_with_superclass_where_clause.swift | 4 ++ ...endable_to_any_for_generic_arguments.swift | 10 +-- test/SILGen/statements.swift | 3 + test/SILGen/switch.swift | 1 + test/SILGen/variadic-generic-results.swift | 1 + utils/sil-mode.el | 5 ++ 30 files changed, 169 insertions(+), 66 deletions(-) diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index eba86e73bd6d2..474cb9b741ac9 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -719,6 +719,7 @@ struct ImmutableAddressUseVerifier { case SILInstructionKind::KeyPathInst: case SILInstructionKind::SwitchEnumAddrInst: case SILInstructionKind::SelectEnumAddrInst: + case SILInstructionKind::IgnoredUseInst: break; case SILInstructionKind::DebugValueInst: if (cast(inst)->hasAddrVal()) diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index e5d11eb8484c0..da87deba43ca0 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1582,38 +1582,34 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, unsigned idx, return nullptr; }; - auto emitInitializer = [&](Expr *initExpr, VarDecl *var, bool forLocalContext, - InitializationPtr &initialization) { - // If an initial value expression was specified by the decl, emit it into - // the initialization. - FullExpr Scope(Cleanups, CleanupLocation(initExpr)); - - if (forLocalContext) { - if (auto *orig = var->getOriginalWrappedProperty()) { - if (auto *initExpr = getWrappedValueExpr(var)) { - auto value = emitRValue(initExpr); - emitApplyOfPropertyWrapperBackingInitializer( + auto *initExpr = PBD->getExecutableInit(idx); + + // If we do not have an explicit initialization expression, just mark the + // initialization as unfinished for DI to resolve. + if (!initExpr) { + return initialization->finishUninitialized(*this); + } + + // Otherwise, an initial value expression was specified by the decl... emit it + // into the initialization. + FullExpr Scope(Cleanups, CleanupLocation(initExpr)); + + auto *singleVar = PBD->getSingleVar(); + bool isLocalSingleVar = + singleVar && singleVar->getDeclContext()->isLocalContext(); + if (isLocalSingleVar) { + if (auto *orig = singleVar->getOriginalWrappedProperty()) { + if (auto *initExpr = getWrappedValueExpr(singleVar)) { + auto value = emitRValue(initExpr); + emitApplyOfPropertyWrapperBackingInitializer( PBD, orig, getForwardingSubstitutionMap(), std::move(value)) .forwardInto(*this, SILLocation(PBD), initialization.get()); - return; - } + return; } } - - emitExprInto(initExpr, initialization.get()); - }; - - auto *singleVar = PBD->getSingleVar(); - if (auto *Init = PBD->getExecutableInit(idx)) { - // If an initial value expression was specified by the decl, emit it into - // the initialization. - bool isLocalVar = - singleVar && singleVar->getDeclContext()->isLocalContext(); - emitInitializer(Init, singleVar, isLocalVar, initialization); - } else { - // Otherwise, mark it uninitialized for DI to resolve. - initialization->finishUninitialized(*this); } + + emitExprInto(initExpr, initialization.get()); } void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *PBD, @@ -2421,18 +2417,23 @@ void BlackHoleInitialization::performPackExpansionInitialization( void BlackHoleInitialization::copyOrInitValueInto(SILGenFunction &SGF, SILLocation loc, ManagedValue value, bool isInit) { - // Normally we do not do anything if we have a black hole - // initialization... but if we have a move only object, insert a move value. - if (!value.getType().isMoveOnly()) + // If we do not have a noncopyable type, just insert an ignored use. + if (!value.getType().isMoveOnly()) { + SGF.B.createIgnoredUse(loc, value.getValue()); return; + } - // If we have an address, then this will create a new temporary allocation - // which will trigger the move checker. If we have an object though, we need - // to insert an extra move_value to make sure the object checker behaves - // correctly. + // If we have a noncopyable type, we need to do a little more work to satisfy + // the move checkers. If we have an address, then this will create a new + // temporary allocation which will trigger the move checker... value = value.ensurePlusOne(SGF, loc); - if (value.getType().isAddress()) + if (value.getType().isAddress()) { + SGF.B.createIgnoredUse(loc, value.getValue()); return; + } + // If we have an object though, we need to insert an extra move_value to make + // sure the object checker behaves correctly. value = SGF.B.createMoveValue(loc, value); + SGF.B.createIgnoredUse(loc, value.getValue()); } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 410dccecc67a5..90773018b47c8 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -5307,7 +5307,19 @@ static void emitSimpleAssignment(SILGenFunction &SGF, SILLocation loc, value = value.ensurePlusOne(SGF, loc); if (value.getType().isObject()) value = SGF.B.createMoveValue(loc, value); + SGF.B.createIgnoredUse(loc, value.getValue()); + return; + } + + // Emit the ignored use instruction like we would do in emitIgnoredExpr. + if (!rv.isNull()) { + SmallVector values; + std::move(rv).getAll(values); + for (auto v : values) { + SGF.B.createIgnoredUse(loc, v.getValue()); + } } + return; } @@ -6958,7 +6970,24 @@ void SILGenFunction::emitIgnoredExpr(Expr *E) { // Otherwise, emit the result (to get any side effects), but produce it at +0 // if that allows simplification. - emitRValue(E, SGFContext::AllowImmediatePlusZero); + RValue rv = emitRValue(E, SGFContext::AllowImmediatePlusZero); + + // Return early if we do not have any result. + if (rv.isNull()) + return; + + // Then emit ignored use on all of the rvalue components. We purposely do not + // implode the tuple since if we have a tuple formation like: + // + // let _ = (x, y) + // + // we want the use to be on x and y and not on the imploded tuple. It also + // helps us avoid emitting unnecessary code. + SmallVector values; + std::move(rv).getAll(values); + for (auto v : values) { + B.createIgnoredUse(E, v.getValue()); + } } /// Emit the given expression as an r-value, then (if it is a tuple), combine diff --git a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp index 874f1a9f4d256..efd0b02b7c64c 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp @@ -775,6 +775,14 @@ static bool simplifyBlocksWithCallsToNoReturn(SILBasicBlock &BB, if (isa(currInst)) return false; + // If we have an ignored use whose operand is our no return call, ignore it. + if (auto *i = dyn_cast(currInst)) { + if (auto *svi = dyn_cast(i->getOperand()); + svi && getAsCallToNoReturn(svi)) { + return false; + } + } + // destroy_value [dead_end] instructions are inserted at the availability // boundary by lifetime completion. Such instructions correctly mark the // lifetime boundary of the destroyed value and never arise from dead user diff --git a/lib/SILOptimizer/Mandatory/LowerTupleAddrConstructor.cpp b/lib/SILOptimizer/Mandatory/LowerTupleAddrConstructor.cpp index fd39a9f17baba..59ba77c83d5c5 100644 --- a/lib/SILOptimizer/Mandatory/LowerTupleAddrConstructor.cpp +++ b/lib/SILOptimizer/Mandatory/LowerTupleAddrConstructor.cpp @@ -78,17 +78,24 @@ class LowerTupleAddrConstructorTransform : public SILFunctionTransform { bool deletedInst = false; for (auto &block : *function) { for (auto ii = block.begin(), ie = block.end(); ii != ie;) { - auto *inst = dyn_cast(&*ii); + auto *inst = &*ii; ++ii; - if (!inst) + if (auto *i = dyn_cast(inst)) { + i->eraseFromParent(); + deletedInst = true; + continue; + } + + auto *t = dyn_cast(inst); + if (!t) continue; // (tuple_addr_constructor [assign/init] %addr, // (destructure_tuple %tuple)) // -> // (store [assign/init] %tuple to %addr) - if (peepholeTupleDestructorOperand(inst)) { + if (peepholeTupleDestructorOperand(t)) { continue; } @@ -96,17 +103,17 @@ class LowerTupleAddrConstructorTransform : public SILFunctionTransform { unsigned count = 0; visitExplodedTupleValue( - inst->getDest(), + t->getDest(), [&](SILValue value, std::optional index) -> SILValue { if (!index) { - SILValue elt = inst->getElement(count); + SILValue elt = t->getElement(count); if (elt->getType().isAddress()) { - builder.createCopyAddr(inst->getLoc(), elt, value, IsTake, - inst->isInitializationOfDest()); + builder.createCopyAddr(t->getLoc(), elt, value, IsTake, + t->isInitializationOfDest()); } else { builder.emitStoreValueOperation( - inst->getLoc(), elt, value, - bool(inst->isInitializationOfDest()) + t->getLoc(), elt, value, + bool(t->isInitializationOfDest()) ? StoreOwnershipQualifier::Init : StoreOwnershipQualifier::Assign); } @@ -114,10 +121,10 @@ class LowerTupleAddrConstructorTransform : public SILFunctionTransform { return value; } auto *teai = - builder.createTupleElementAddr(inst->getLoc(), value, *index); + builder.createTupleElementAddr(t->getLoc(), value, *index); return teai; }); - inst->eraseFromParent(); + t->eraseFromParent(); deletedInst = true; } } diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyWrappedTypeEliminator.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyWrappedTypeEliminator.cpp index a2fafd333642b..d76d858557cb3 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyWrappedTypeEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyWrappedTypeEliminator.cpp @@ -210,6 +210,7 @@ struct SILMoveOnlyWrappedTypeEliminatorVisitor NO_UPDATE_NEEDED(AddressToPointer) NO_UPDATE_NEEDED(ExistentialMetatype) NO_UPDATE_NEEDED(Builtin) + NO_UPDATE_NEEDED(IgnoredUse) #undef NO_UPDATE_NEEDED bool eliminateIdentityCast(SingleValueInstruction *svi) { diff --git a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp index cd951270ab317..84948c8fd6e97 100644 --- a/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp +++ b/lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp @@ -139,6 +139,11 @@ struct OwnershipModelEliminatorVisitor bool visitExplicitCopyAddrInst(ExplicitCopyAddrInst *cai); bool visitApplyInst(ApplyInst *ai); + bool visitIgnoredUseInst(IgnoredUseInst *iui) { + eraseInstruction(iui); + return true; + } + void splitDestroy(DestroyValueInst *destroy); bool peepholeTupleConstructorUser(DestructureTupleInst *dti); bool visitDestroyValueInst(DestroyValueInst *dvi); diff --git a/test/AutoDiff/SILOptimizer/activity_analysis.swift b/test/AutoDiff/SILOptimizer/activity_analysis.swift index 986fb6e8da3a3..d57583cbfe487 100644 --- a/test/AutoDiff/SILOptimizer/activity_analysis.swift +++ b/test/AutoDiff/SILOptimizer/activity_analysis.swift @@ -191,7 +191,7 @@ func checked_cast_addr_nonactive_result(_ x: T) -> T { // CHECK: bb5: // CHECK: [VARIED] %18 = argument of bb5 : $Float // CHECK: bb6: -// CHECK: [NONE] %22 = tuple () +// CHECK: [NONE] %{{.*}} = tuple () // CHECK-LABEL: sil hidden [ossa] @${{.*}}checked_cast_addr_nonactive_result{{.*}} : $@convention(thin) (@in_guaranteed T) -> @out T { // CHECK: checked_cast_addr_br take_always T in %3 : $*T to Float in %5 : $*Float, bb1, bb2 diff --git a/test/Concurrency/transfernonsendable.swift b/test/Concurrency/transfernonsendable.swift index 969c66d57c43e..54a0652fd62c9 100644 --- a/test/Concurrency/transfernonsendable.swift +++ b/test/Concurrency/transfernonsendable.swift @@ -887,8 +887,8 @@ func letSendableNonTrivialLetStructFieldTest() async { await transferToMain(test) // expected-tns-warning {{sending 'test' risks causing data races}} // expected-tns-note @-1 {{sending 'test' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}} // expected-complete-warning @-2 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} - _ = test.letSendableNonTrivial - useValue(test) // expected-tns-note {{access can happen concurrently}} + _ = test.letSendableNonTrivial // expected-tns-note {{access can happen concurrently}} + useValue(test) } func letNonSendableNonTrivialLetStructFieldTest() async { @@ -968,8 +968,8 @@ func varSendableNonTrivialLetStructFieldTest() async { await transferToMain(test) // expected-tns-warning {{sending 'test' risks causing data races}} // expected-tns-note @-1 {{sending 'test' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}} // expected-complete-warning @-2 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}} - _ = test.varSendableNonTrivial - useValue(test) // expected-tns-note {{access can happen concurrently}} + _ = test.varSendableNonTrivial // expected-tns-note {{access can happen concurrently}} + useValue(test) } func varNonSendableNonTrivialLetStructFieldTest() async { diff --git a/test/SILGen/class_bound_protocols.swift b/test/SILGen/class_bound_protocols.swift index e0f78d43b23d1..5e1a1c1dc28ae 100644 --- a/test/SILGen/class_bound_protocols.swift +++ b/test/SILGen/class_bound_protocols.swift @@ -198,6 +198,7 @@ func takesInheritsMutatingMethod(x: inout InheritsMutatingMethod, // CHECK-NEXT: end_borrow // CHECK-NEXT: destroy_addr // CHECK-NEXT: end_access [[X_ADDR]] : $*any InheritsMutatingMethod + // CHECK-NEXT: ignored_use [[RESULT_VALUE]] // CHECK-NEXT: dealloc_stack [[TEMPORARY]] : $*@opened("{{.*}}", any InheritsMutatingMethod) Self _ = x.mutatingCounter diff --git a/test/SILGen/do_expr.swift b/test/SILGen/do_expr.swift index 2b4a264f75ccd..e70d1f3206a5e 100644 --- a/test/SILGen/do_expr.swift +++ b/test/SILGen/do_expr.swift @@ -56,6 +56,7 @@ func test6() -> Int { // CHECK: try_apply [[THROWS_ERR_FN]]({{%[0-9]+}}) : $@convention(thin) (Int) -> (Int, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]] // // CHECK: [[BB_NORMAL]] +// CHECK-NEXT: ignored_use // CHECK-NEXT: store [[MVY]] to [trivial] [[RESULT]] : $*Int // CHECK-NEXT: extend_lifetime [[MVY]] : $Int // CHECK-NEXT: br [[BB_EXIT:bb[0-9]+]] diff --git a/test/SILGen/enum.swift b/test/SILGen/enum.swift index f21bdcf2968f2..b3f16ac881d35 100644 --- a/test/SILGen/enum.swift +++ b/test/SILGen/enum.swift @@ -10,6 +10,7 @@ enum Boolish { func Boolish_cases() { // CHECK: [[BOOLISH:%[0-9]+]] = metatype $@thin Boolish.Type // CHECK-NEXT: [[FALSY:%[0-9]+]] = enum $Boolish, #Boolish.falsy!enumelt + // CHECK-NEXT: ignored_use _ = Boolish.falsy // CHECK-NEXT: [[BOOLISH:%[0-9]+]] = metatype $@thin Boolish.Type @@ -52,12 +53,14 @@ func AddressOnly_cases(_ s: S) { // CHECK: [[METATYPE:%.*]] = metatype $@thin AddressOnly.Type // CHECK: [[FN:%.*]] = function_ref @$s4enum17AddressOnly_casesyyAA1SVFAA0bC0OAA1P_pcAFmcfu_ // CHECK-NEXT: [[CTOR:%.*]] = apply [[FN]]([[METATYPE]]) + // CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_value [[CTOR]] _ = AddressOnly.mere // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin AddressOnly.Type // CHECK-NEXT: [[NOUGHT:%.*]] = alloc_stack $AddressOnly // CHECK-NEXT: inject_enum_addr [[NOUGHT]] + // CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[NOUGHT]] // CHECK-NEXT: dealloc_stack [[NOUGHT]] _ = AddressOnly.nought @@ -68,6 +71,7 @@ func AddressOnly_cases(_ s: S) { // CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = init_existential_addr [[PAYLOAD]] // CHECK-NEXT: store %0 to [trivial] [[PAYLOAD_ADDR]] // CHECK-NEXT: inject_enum_addr [[MERE]] + // CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[MERE]] // CHECK-NEXT: dealloc_stack [[MERE]] _ = AddressOnly.mere(s) @@ -79,6 +83,7 @@ func AddressOnly_cases(_ s: S) { // CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[PHANTOM]] : $*AddressOnly, #AddressOnly.phantom!enumelt // CHECK-NEXT: store %0 to [trivial] [[PAYLOAD]] // CHECK-NEXT: inject_enum_addr [[PHANTOM]] : $*AddressOnly, #AddressOnly.phantom!enumelt + // CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[PHANTOM]] // CHECK-NEXT: dealloc_stack [[PHANTOM]] @@ -97,6 +102,7 @@ func PolyOptionable_cases(_ t: T) { // CHECK: [[METATYPE:%.*]] = metatype $@thin PolyOptionable.Type // CHECK-NEXT: [[NOUGHT:%.*]] = alloc_stack $PolyOptionable // CHECK-NEXT: inject_enum_addr [[NOUGHT]] +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[NOUGHT]] // CHECK-NEXT: dealloc_stack [[NOUGHT]] _ = PolyOptionable.nought @@ -106,6 +112,7 @@ func PolyOptionable_cases(_ t: T) { // CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[MERE]] // CHECK-NEXT: copy_addr %0 to [init] [[PAYLOAD]] : $*T // CHECK-NEXT: inject_enum_addr [[MERE]] +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[MERE]] // CHECK-NEXT: dealloc_stack [[MERE]] _ = PolyOptionable.mere(t) @@ -122,6 +129,7 @@ func PolyOptionable_specialized_cases(_ t: Int) { // CHECK: [[METATYPE:%.*]] = metatype $@thin PolyOptionable.Type // CHECK-NEXT: [[NOUGHT:%.*]] = enum $PolyOptionable, #PolyOptionable.nought!enumelt +// CHECK-NEXT: ignored_use _ = PolyOptionable.nought // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin PolyOptionable.Type diff --git a/test/SILGen/errors.swift b/test/SILGen/errors.swift index b5cf0117af0a0..5fc7af3440de2 100644 --- a/test/SILGen/errors.swift +++ b/test/SILGen/errors.swift @@ -530,6 +530,8 @@ func testForceTryMultiple() { // CHECK: [[FN_2:%.+]] = function_ref @$s6errors10make_a_catAA3CatCyKF // CHECK-NEXT: try_apply [[FN_2]]() : $@convention(thin) () -> (@owned Cat, @error any Error), normal [[SUCCESS_2:[^ ]+]], error [[CLEANUPS_2:[^ ]+]], // CHECK: [[SUCCESS_2]]([[VALUE_2:%.+]] : @owned $Cat) +// CHECK-NEXT: ignored_use [[VALUE_1]] +// CHECK-NEXT: ignored_use [[VALUE_2]] // CHECK-NEXT: destroy_value [[VALUE_2]] : $Cat // CHECK-NEXT: destroy_value [[VALUE_1]] : $Cat // CHECK-NEXT: [[VOID:%.+]] = tuple () @@ -841,6 +843,7 @@ func testForcePeephole(_ f: () throws -> Int?) -> Int { // CHECK-NEXT: [[WRAPPED:%.+]] = enum $Optional, #Optional.some!enumelt, [[VALUE]] // CHECK-NEXT: br [[DONE:[^ ]+]]([[WRAPPED]] : $Optional) // CHECK: [[DONE]]([[RESULT:%.+]] : @owned $Optional): +// CHECK-NEXT: ignored_use [[RESULT]] // CHECK-NEXT: destroy_value [[RESULT]] : $Optional // CHECK-NEXT: [[VOID:%.+]] = tuple () // CHECK-NEXT: return [[VOID]] : $() @@ -897,6 +900,7 @@ func testOptionalTryVar() { // CHECK-NEXT: inject_enum_addr [[BOX]] : $*Optional, #Optional.some!enumelt // CHECK-NEXT: br [[DONE:[^ ]+]], // CHECK: [[DONE]]: +// CHECK-NEXT: ignored_use [[BOX]] // CHECK-NEXT: destroy_addr [[BOX]] : $*Optional // CHECK-NEXT: dealloc_stack [[BOX]] : $*Optional // CHECK-NOT: destroy_addr %0 : $*T @@ -949,6 +953,7 @@ func testOptionalTryAddressOnlyVar(_ obj: T) { // CHECK-NEXT: [[WRAPPED:%.+]] = enum $Optional<(Cat, Cat)>, #Optional.some!enumelt, [[TUPLE]] // CHECK-NEXT: br [[DONE:[^ ]+]]([[WRAPPED]] : $Optional<(Cat, Cat)>) // CHECK: [[DONE]]([[RESULT:%.+]] : @owned $Optional<(Cat, Cat)>): +// CHECK-NEXT: ignored_use [[RESULT]] // CHECK-NEXT: destroy_value [[RESULT]] : $Optional<(Cat, Cat)> // CHECK-NEXT: [[VOID:%.+]] = tuple () // CHECK-NEXT: return [[VOID]] : $() @@ -970,6 +975,7 @@ func testOptionalTryMultiple() { // CHECK: bb0: // CHECK-NEXT: [[VALUE:%.+]] = tuple () // CHECK-NEXT: = enum $Optional<()>, #Optional.some!enumelt, [[VALUE]] +// CHECK-NEXT: ignored_use // CHECK-NEXT: [[VOID:%.+]] = tuple () // CHECK-NEXT: return [[VOID]] : $() // CHECK: } // end sil function '$s6errors25testOptionalTryNeverFailsyyF' @@ -1000,6 +1006,7 @@ func testOptionalTryNeverFailsVar() { // CHECK-NEXT: [[BOX_DATA:%.+]] = init_enum_data_addr [[BOX]] : $*Optional, #Optional.some!enumelt // CHECK-NEXT: copy_addr %0 to [init] [[BOX_DATA]] : $*T // CHECK-NEXT: inject_enum_addr [[BOX]] : $*Optional, #Optional.some!enumelt +// CHECK-NEXT: ignored_use [[BOX]] // CHECK-NEXT: destroy_addr [[BOX]] : $*Optional // CHECK-NEXT: dealloc_stack [[BOX]] : $*Optional // CHECK-NOT: destroy_addr %0 : $*T diff --git a/test/SILGen/expressions.swift b/test/SILGen/expressions.swift index a8f7db56dfd56..709444ece8a8c 100644 --- a/test/SILGen/expressions.swift +++ b/test/SILGen/expressions.swift @@ -541,6 +541,7 @@ func dontEmitIgnoredLoadExpr(_ a: NonTrivialStruct) -> NonTrivialStruct.Type { // CHECK-LABEL: dontEmitIgnoredLoadExpr // CHECK: bb0(%0 : @guaranteed $NonTrivialStruct): // CHECK-NEXT: debug_value +// CHECK-NEXT: ignored_use // CHECK-NEXT: [[RESULT:%.*]] = metatype $@thin NonTrivialStruct.Type // CHECK-NEXT: return [[RESULT]] : $@thin NonTrivialStruct.Type diff --git a/test/SILGen/extern_c.swift b/test/SILGen/extern_c.swift index dc27d977de76e..cab881c4b2a61 100644 --- a/test/SILGen/extern_c.swift +++ b/test/SILGen/extern_c.swift @@ -43,7 +43,7 @@ func main() { _ = withoutCName() // CHECK-DAG: [[F6:%.+]] = function_ref @$s8extern_c10defaultArgyySiFfA_ : $@convention(thin) () -> Int // CHECK-DAG: [[DEFAULT_V:%.+]] = apply [[F6]]() : $@convention(thin) () -> Int - // CHECK-DAG: [[F7:%.+]] = function_ref @default_arg : $@convention(c) (Int) -> () // user: %20 + // CHECK-DAG: [[F7:%.+]] = function_ref @default_arg : $@convention(c) (Int) -> () // CHECK-DAG: apply [[F7]]([[DEFAULT_V]]) : $@convention(c) (Int) -> () defaultArg() } diff --git a/test/SILGen/indirect_enum.swift b/test/SILGen/indirect_enum.swift index ddec01dd1b7cf..a8cb3433aa012 100644 --- a/test/SILGen/indirect_enum.swift +++ b/test/SILGen/indirect_enum.swift @@ -12,6 +12,7 @@ func TreeA_cases(_ t: T, l: TreeA, r: TreeA) { // CHECK: bb0([[ARG1:%.*]] : $*T, [[ARG2:%.*]] : @guaranteed $TreeA, [[ARG3:%.*]] : @guaranteed $TreeA): // CHECK: [[METATYPE:%.*]] = metatype $@thin TreeA.Type // CHECK-NEXT: [[NIL:%.*]] = enum $TreeA, #TreeA.Nil!enumelt +// CHECK-NEXT: ignored_use // CHECK-NOT: destroy_value [[NIL]] let _ = TreeA.Nil @@ -20,6 +21,7 @@ func TreeA_cases(_ t: T, l: TreeA, r: TreeA) { // CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]] // CHECK-NEXT: copy_addr [[ARG1]] to [init] [[PB]] // CHECK-NEXT: [[LEAF:%.*]] = enum $TreeA, #TreeA.Leaf!enumelt, [[BOX]] +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_value [[LEAF]] let _ = TreeA.Leaf(t) @@ -33,6 +35,7 @@ func TreeA_cases(_ t: T, l: TreeA, r: TreeA) { // CHECK-NEXT: [[ARG3_COPY:%.*]] = copy_value [[ARG3]] // CHECK-NEXT: store [[ARG3_COPY]] to [init] [[RIGHT]] // CHECK-NEXT: [[BRANCH:%.*]] = enum $TreeA, #TreeA.Branch!enumelt, [[BOX]] +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_value [[BRANCH]] let _ = TreeA.Branch(left: l, right: r) @@ -51,6 +54,7 @@ func TreeA_reabstract(_ f: @escaping (Int) -> Int) { // CHECK-NEXT: [[FNC:%.*]] = convert_function [[FN]] // CHECK-NEXT: store [[FNC]] to [init] [[PB]] // CHECK-NEXT: [[LEAF:%.*]] = enum $TreeA<(Int) -> Int>, #TreeA.Leaf!enumelt, [[BOX]] +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_value [[LEAF]] // CHECK: return let _ = TreeA<(Int) -> Int>.Leaf(f) @@ -69,6 +73,7 @@ func TreeB_cases(_ t: T, l: TreeB, r: TreeB) { // CHECK: [[METATYPE:%.*]] = metatype $@thin TreeB.Type // CHECK: [[NIL:%.*]] = alloc_stack $TreeB // CHECK-NEXT: inject_enum_addr [[NIL]] : $*TreeB, #TreeB.Nil!enumelt +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[NIL]] // CHECK-NEXT: dealloc_stack [[NIL]] let _ = TreeB.Nil @@ -78,6 +83,7 @@ func TreeB_cases(_ t: T, l: TreeB, r: TreeB) { // CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[LEAF]] : $*TreeB, #TreeB.Leaf!enumelt // CHECK-NEXT: copy_addr %0 to [init] [[PAYLOAD]] // CHECK-NEXT: inject_enum_addr [[LEAF]] : $*TreeB, #TreeB.Leaf!enumelt +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[LEAF]] // CHECK-NEXT: dealloc_stack [[LEAF]] let _ = TreeB.Leaf(t) @@ -93,6 +99,7 @@ func TreeB_cases(_ t: T, l: TreeB, r: TreeB) { // CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[BRANCH]] // CHECK-NEXT: store [[BOX]] to [init] [[PAYLOAD]] // CHECK-NEXT: inject_enum_addr [[BRANCH]] : $*TreeB, #TreeB.Branch!enumelt +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[BRANCH]] // CHECK-NEXT: dealloc_stack [[BRANCH]] let _ = TreeB.Branch(left: l, right: r) @@ -105,11 +112,13 @@ func TreeInt_cases(_ t: Int, l: TreeInt, r: TreeInt) { // CHECK: bb0([[ARG1:%.*]] : $Int, [[ARG2:%.*]] : @guaranteed $TreeInt, [[ARG3:%.*]] : @guaranteed $TreeInt): // CHECK: [[METATYPE:%.*]] = metatype $@thin TreeInt.Type // CHECK-NEXT: [[NIL:%.*]] = enum $TreeInt, #TreeInt.Nil!enumelt +// CHECK-NEXT: ignored_use // CHECK-NOT: destroy_value [[NIL]] let _ = TreeInt.Nil // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeInt.Type // CHECK-NEXT: [[LEAF:%.*]] = enum $TreeInt, #TreeInt.Leaf!enumelt, [[ARG1]] +// CHECK-NEXT: ignored_use // CHECK-NOT: destroy_value [[LEAF]] let _ = TreeInt.Leaf(t) @@ -123,6 +132,7 @@ func TreeInt_cases(_ t: Int, l: TreeInt, r: TreeInt) { // CHECK-NEXT: [[ARG3_COPY:%.*]] = copy_value [[ARG3]] : $TreeInt // CHECK-NEXT: store [[ARG3_COPY]] to [init] [[RIGHT]] // CHECK-NEXT: [[BRANCH:%.*]] = enum $TreeInt, #TreeInt.Branch!enumelt, [[BOX]] +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_value [[BRANCH]] let _ = TreeInt.Branch(left: l, right: r) } diff --git a/test/SILGen/initializers.swift b/test/SILGen/initializers.swift index 35d432bf7e4f3..7aa418461f910 100644 --- a/test/SILGen/initializers.swift +++ b/test/SILGen/initializers.swift @@ -1086,6 +1086,7 @@ class ThrowDerivedClass : ThrowBaseClass { // CHECK-NEXT: try_apply {{.*}}({{.*}}) : $@convention(thin) (Int) -> (Int, @error any Error), normal [[SUCC_BB2:bb[0-9]+]], error [[ERROR_BB2:bb[0-9]+]] // // CHECK: [[SUCC_BB2]]( + // CHECK-NEXT: ignored_use // CHECK-NEXT: [[RESULT:%.*]] = load [copy] [[PB_BOX]] // CHECK-NEXT: end_borrow [[SELF_LIFETIME]] // CHECK-NEXT: destroy_value [[MARKED_SELF_BOX]] diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index 3103db1d8b13d..c4fb033c33084 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -656,19 +656,19 @@ class M { // CHECK-LABEL: // test_metatype_keypaths() // CHECK-LABEL: sil hidden [ossa] @{{.*}} : $@convention(thin) () -> () { func test_metatype_keypaths() { - // CHECK: keypath $ReferenceWritableKeyPath, (root $M.Type; settable_property $Int, id @$s8keypaths1MC10chanceRainSivgZ : $@convention(method) (@thick M.Type) -> Int, getter @$s8keypaths1MC10chanceRainSivpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Int, setter @$s8keypaths1MC10chanceRainSivpZACmTk : $@convention(keypath_accessor_setter) (@in_guaranteed Int, @in_guaranteed @thick M.Type) -> ()) // user: %1 + // CHECK: keypath $ReferenceWritableKeyPath, (root $M.Type; settable_property $Int, id @$s8keypaths1MC10chanceRainSivgZ : $@convention(method) (@thick M.Type) -> Int, getter @$s8keypaths1MC10chanceRainSivpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Int, setter @$s8keypaths1MC10chanceRainSivpZACmTk : $@convention(keypath_accessor_setter) (@in_guaranteed Int, @in_guaranteed @thick M.Type) -> ()) let _: KeyPath = \M.Type.chanceRain - // CHECK: keypath $KeyPath, (root $M.Type; gettable_property $Bool, id @$s8keypaths1MC7isSunnySbvgZ : $@convention(method) (@thick M.Type) -> Bool, getter @$s8keypaths1MC7isSunnySbvpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Bool) // user: %4 + // CHECK: keypath $KeyPath, (root $M.Type; gettable_property $Bool, id @$s8keypaths1MC7isSunnySbvgZ : $@convention(method) (@thick M.Type) -> Bool, getter @$s8keypaths1MC7isSunnySbvpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Bool) let _: KeyPath = \M.Type.isSunny - // CHECK: keypath $ReferenceWritableKeyPath, (root $M.Type; settable_property $Bool, id @$s8keypaths1MC8isCloudySbvgZ : $@convention(method) (@thick M.Type) -> Bool, getter @$s8keypaths1MC8isCloudySbvpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Bool, setter @$s8keypaths1MC8isCloudySbvpZACmTk : $@convention(keypath_accessor_setter) (@in_guaranteed Bool, @in_guaranteed @thick M.Type) -> ()) // user: %6 + // CHECK: keypath $ReferenceWritableKeyPath, (root $M.Type; settable_property $Bool, id @$s8keypaths1MC8isCloudySbvgZ : $@convention(method) (@thick M.Type) -> Bool, getter @$s8keypaths1MC8isCloudySbvpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type) -> @out Bool, setter @$s8keypaths1MC8isCloudySbvpZACmTk : $@convention(keypath_accessor_setter) (@in_guaranteed Bool, @in_guaranteed @thick M.Type) -> ()) let _: KeyPath = \M.Type.isCloudy - // CHECK: keypath $KeyPath, (root $M.Type; gettable_property $String, id @$s8keypaths1MCySSSicigZ : $@convention(method) (Int, @thick M.Type) -> @owned String, getter @$s8keypaths1MCySSSicipZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type, @in_guaranteed Int) -> @out String, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) (%11) // user: %13 + // CHECK: keypath $KeyPath, (root $M.Type; gettable_property $String, id @$s8keypaths1MCySSSicigZ : $@convention(method) (Int, @thick M.Type) -> @owned String, getter @$s8keypaths1MCySSSicipZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type, @in_guaranteed Int) -> @out String, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) (%{{.*}}) let _: KeyPath = \M.Type.[2] - // CHECK: keypath $KeyPath, (root $M; gettable_property $N.Type, id #M.subscript!getter : (M) -> (Int) -> N.Type, getter @$s8keypaths1MCyAA1NVmSicipACTK : $@convention(keypath_accessor_getter) (@in_guaranteed M, @in_guaranteed Int) -> @out @thick N.Type, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) (%17) // user: %19 + // CHECK: keypath $KeyPath, (root $M; gettable_property $N.Type, id #M.subscript!getter : (M) -> (Int) -> N.Type, getter @$s8keypaths1MCyAA1NVmSicipACTK : $@convention(keypath_accessor_getter) (@in_guaranteed M, @in_guaranteed Int) -> @out @thick N.Type, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) (%{{.*}}) let _: KeyPath = \M.[76] - // CHECK: keypath $KeyPath, (root $M.Type; gettable_property $N.Type, id @$s8keypaths1MCyAA1NVmSicigZ : $@convention(method) (Int, @thick M.Type) -> @thin N.Type, getter @$s8keypaths1MCyAA1NVmSicipZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type, @in_guaranteed Int) -> @out @thick N.Type, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) (%23) // user: %25 + // CHECK: keypath $KeyPath, (root $M.Type; gettable_property $N.Type, id @$s8keypaths1MCyAA1NVmSicigZ : $@convention(method) (Int, @thick M.Type) -> @thin N.Type, getter @$s8keypaths1MCyAA1NVmSicipZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick M.Type, @in_guaranteed Int) -> @out @thick N.Type, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(keypath_accessor_equals) (@in_guaranteed Int, @in_guaranteed Int) -> Bool, indices_hash @$sSiTh : $@convention(keypath_accessor_hash) (@in_guaranteed Int) -> Int) (%{{.*}}) let _: KeyPath = \M.Type.[76] - // CHECK: keypath $KeyPath>, (root $M; gettable_property $Optional, id #M.degrees!getter : (M) -> () -> N.Type?, getter @$s8keypaths1MC7degreesAA1NVmSgvpACTK : $@convention(keypath_accessor_getter) (@in_guaranteed M) -> @out Optional<@thick N.Type>; optional_chain : $N.Type; gettable_property $Int, id @$s8keypaths1NV6kelvinSivgZ : $@convention(method) (@thin N.Type) -> Int, getter @$s8keypaths1NV6kelvinSivpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick N.Type) -> @out Int; optional_wrap : $Optional) // user: %27 + // CHECK: keypath $KeyPath>, (root $M; gettable_property $Optional, id #M.degrees!getter : (M) -> () -> N.Type?, getter @$s8keypaths1MC7degreesAA1NVmSgvpACTK : $@convention(keypath_accessor_getter) (@in_guaranteed M) -> @out Optional<@thick N.Type>; optional_chain : $N.Type; gettable_property $Int, id @$s8keypaths1NV6kelvinSivgZ : $@convention(method) (@thin N.Type) -> Int, getter @$s8keypaths1NV6kelvinSivpZACmTK : $@convention(keypath_accessor_getter) (@in_guaranteed @thick N.Type) -> @out Int; optional_wrap : $Optional) let _: KeyPath = \.degrees?.kelvin } diff --git a/test/SILGen/lifetime_unions.swift b/test/SILGen/lifetime_unions.swift index f2d48b2b283b6..c6fdb280c9a2a 100644 --- a/test/SILGen/lifetime_unions.swift +++ b/test/SILGen/lifetime_unions.swift @@ -47,6 +47,7 @@ func getAddressOnlyUnion(_: T.Type) -> AddressOnlyUnion { return .Foo } func destroyUnionRValues() { // CHECK: [[GET_TRIVIAL_UNION:%.*]] = function_ref @$s15lifetime_unions15getTrivialUnionAA0dE0OyF : $@convention(thin) () -> TrivialUnion // CHECK: [[TRIVIAL_UNION:%.*]] = apply [[GET_TRIVIAL_UNION]]() : $@convention(thin) () -> TrivialUnion + // CHECK-NEXT: ignored_use [[TRIVIAL_UNION]] // CHECK-NOT: [[TRIVIAL_UNION]] getTrivialUnion() diff --git a/test/SILGen/metatypes.swift b/test/SILGen/metatypes.swift index ef2285c9a3f55..e892367f6c33d 100644 --- a/test/SILGen/metatypes.swift +++ b/test/SILGen/metatypes.swift @@ -94,6 +94,7 @@ func existential_metatype_from_thin() -> Any.Type { // CHECK-NEXT: [[T2:%.*]] = apply [[T0]]([[T1]]) // CHECK-NEXT: [[MV:%.*]] = move_value [var_decl] [[T2]] : $SomeStruct // CHECK-NEXT: debug_value [[MV]] : $SomeStruct, let, name "s" +// CHECK-NEXT: ignored_use // CHECK-NEXT: [[T0:%.*]] = metatype $@thin SomeStruct.Type // CHECK-NEXT: [[T1:%.*]] = metatype $@thick SomeStruct.Type // CHECK-NEXT: [[T2:%.*]] = init_existential_metatype [[T1]] : $@thick SomeStruct.Type, $@thick any Any.Type diff --git a/test/SILGen/optional.swift b/test/SILGen/optional.swift index bfa0399b79feb..eeee89623ca2d 100644 --- a/test/SILGen/optional.swift +++ b/test/SILGen/optional.swift @@ -177,6 +177,7 @@ func implicit_iuo_unwrap_sourceLocation(_ value: Int!) { // CHECK: br bb2 // CHECK: bb2: +// CHECK-NEXT: ignored_use [[MEM]] // CHECK-NEXT: destroy_addr [[MEM]] : $*Any // CHECK-NEXT: dealloc_stack [[MEM]] : $*Any // CHECK: return diff --git a/test/SILGen/partial_apply_protocol.swift b/test/SILGen/partial_apply_protocol.swift index dd207df4df688..96c2cb57e58eb 100644 --- a/test/SILGen/partial_apply_protocol.swift +++ b/test/SILGen/partial_apply_protocol.swift @@ -62,7 +62,7 @@ func testClonableInGenericContext(c: Clonable, t: T) { // CHECK: [[THUNK:%.*]] = apply [[THUNK_FN]]({{.*}}) let _: () -> Clonable = c.clone - // CHECK: [[THUNK_FN:%.*]] = function_ref @$s22partial_apply_protocol28testClonableInGenericContext1c1tyAA0E0_p_xtlFAaE_pSgycAaE_pcfu1_ : $@convention(thin) (@in_guaranteed any Clonable) -> @owned @callee_guaranteed () -> @out Optional // user: %8 + // CHECK: [[THUNK_FN:%.*]] = function_ref @$s22partial_apply_protocol28testClonableInGenericContext1c1tyAA0E0_p_xtlFAaE_pSgycAaE_pcfu1_ : $@convention(thin) (@in_guaranteed any Clonable) -> @owned @callee_guaranteed () -> @out Optional // CHECK: [[THUNK:%.*]] = apply [[THUNK_FN]]({{.*}}) let _: () -> Clonable? = c.maybeClone diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index ab429d86351cd..3a110a812ec10 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -831,6 +831,7 @@ protocol NonmutatingProtocol { // CHECK-NEXT: destroy_value [[C]] : $ReferenceType // CHECK-NEXT: [[GETTER:%.*]] = witness_method $@opened("{{.*}}", any NonmutatingProtocol) Self, #NonmutatingProtocol.x!getter : (Self) -> () -> Int, [[C_FIELD_PAYLOAD]] : $*@opened("{{.*}}", any NonmutatingProtocol) Self : $@convention(witness_method: NonmutatingProtocol) <τ_0_0 where τ_0_0 : NonmutatingProtocol> (@in_guaranteed τ_0_0) -> Int // CHECK-NEXT: [[RESULT_VALUE:%.*]] = apply [[GETTER]]<@opened("{{.*}}", any NonmutatingProtocol) Self>([[C_FIELD_COPY]]) : $@convention(witness_method: NonmutatingProtocol) <τ_0_0 where τ_0_0 : NonmutatingProtocol> (@in_guaranteed τ_0_0) -> Int +// CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[C_FIELD_COPY]] : $*@opened("{{.*}}", any NonmutatingProtocol) Self // CHECK-NEXT: dealloc_stack [[C_FIELD_COPY]] : $*@opened("{{.*}}", any NonmutatingProtocol) Self // CHECK-NEXT: destroy_addr [[C_FIELD_BOX]] : $*any NonmutatingProtocol diff --git a/test/SILGen/protocol_with_superclass.swift b/test/SILGen/protocol_with_superclass.swift index 5136b862ad081..fcd27aecf8de7 100644 --- a/test/SILGen/protocol_with_superclass.swift +++ b/test/SILGen/protocol_with_superclass.swift @@ -46,12 +46,14 @@ extension ProtoRefinesClass { // CHECK: [[SELF:%.*]] = copy_value %3 : $Self // CHECK-NEXT: [[UPCAST:%.*]] = upcast [[SELF]] : $Self to $Generic + // CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_value [[UPCAST]] : $Generic let _: Generic = self // CHECK: [[SELF:%.*]] = copy_value %3 : $Self // CHECK-NEXT: [[UPCAST:%.*]] = upcast [[SELF]] : $Self to $Generic // CHECK-NEXT: [[UPCAST2:%.*]] = upcast [[UPCAST]] : $Generic to $Concrete + // CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_value [[UPCAST2]] : $Concrete let _: Concrete = self @@ -59,6 +61,7 @@ extension ProtoRefinesClass { // CHECK-NEXT: [[SELF:%.*]] = copy_value %3 : $Self // CHECK-NEXT: [[ADDR:%.*]] = init_existential_addr [[BOX]] : $*any BaseProto, $Self // CHECK-NEXT: store [[SELF]] to [init] [[ADDR]] : $*Self + // CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_addr [[BOX]] : $*any BaseProto // CHECK-NEXT: dealloc_stack [[BOX]] : $*any BaseProto let _: BaseProto = self @@ -206,6 +209,7 @@ func useProtocolWithClassInits1(_ t: ProtocolWithClassInits.Type) { // CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]]([[UPCAST]]) // CHECK-NEXT: [[CAST:%.*]] = unchecked_ref_cast [[RESULT]] : $ClassWithInits to $@opened("{{.*}}", any ProtocolWithClassInits) Self // CHECK-NEXT: [[EXISTENTIAL:%.*]] = init_existential_ref [[CAST]] : $@opened("{{.*}}", any ProtocolWithClassInits) Self : $@opened("{{.*}}", any ProtocolWithClassInits) Self, $any ProtocolWithClassInits + // CHECK-NEXT: ignored_use // CHECK-NEXT: destroy_value [[EXISTENTIAL]] let _: ProtocolWithClassInits = t.init(requiredInit: ()) } diff --git a/test/SILGen/protocol_with_superclass_where_clause.swift b/test/SILGen/protocol_with_superclass_where_clause.swift index c8af7f2efa113..d2e4c397f33a8 100644 --- a/test/SILGen/protocol_with_superclass_where_clause.swift +++ b/test/SILGen/protocol_with_superclass_where_clause.swift @@ -46,12 +46,14 @@ extension ProtoRefinesClass { // CHECK: [[SELF:%.*]] = copy_value %3 : $Self // CHECK-NEXT: [[UPCAST:%.*]] = upcast [[SELF]] : $Self to $Generic + // CHECK-NEXT: ignored_use [[UPCAST]] // CHECK-NEXT: destroy_value [[UPCAST]] : $Generic let _: Generic = self // CHECK: [[SELF:%.*]] = copy_value %3 : $Self // CHECK-NEXT: [[UPCAST:%.*]] = upcast [[SELF]] : $Self to $Generic // CHECK-NEXT: [[UPCAST2:%.*]] = upcast [[UPCAST]] : $Generic to $Concrete + // CHECK-NEXT: ignored_use [[UPCAST2]] // CHECK-NEXT: destroy_value [[UPCAST2]] : $Concrete let _: Concrete = self @@ -59,6 +61,7 @@ extension ProtoRefinesClass { // CHECK-NEXT: [[SELF:%.*]] = copy_value %3 : $Self // CHECK-NEXT: [[ADDR:%.*]] = init_existential_addr [[BOX]] : $*any BaseProto, $Self // CHECK-NEXT: store [[SELF]] to [init] [[ADDR]] : $*Self + // CHECK-NEXT: ignored_use [[BOX]] // CHECK-NEXT: destroy_addr [[BOX]] : $*any BaseProto // CHECK-NEXT: dealloc_stack [[BOX]] : $*any BaseProto let _: BaseProto = self @@ -206,6 +209,7 @@ func useProtocolWithClassInits1(_ t: ProtocolWithClassInits.Type) { // CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]]([[UPCAST]]) // CHECK-NEXT: [[CAST:%.*]] = unchecked_ref_cast [[RESULT]] : $ClassWithInits to $@opened("{{.*}}", any ProtocolWithClassInits) Self // CHECK-NEXT: [[EXISTENTIAL:%.*]] = init_existential_ref [[CAST]] : $@opened("{{.*}}", any ProtocolWithClassInits) Self : $@opened("{{.*}}", any ProtocolWithClassInits) Self, $any ProtocolWithClassInits + // CHECK-NEXT: ignored_use [[EXISTENTIAL]] // CHECK-NEXT: destroy_value [[EXISTENTIAL]] let _: ProtocolWithClassInits = t.init(requiredInit: ()) } diff --git a/test/SILGen/sendable_to_any_for_generic_arguments.swift b/test/SILGen/sendable_to_any_for_generic_arguments.swift index 66e5fdb312582..9c5ee5823b878 100644 --- a/test/SILGen/sendable_to_any_for_generic_arguments.swift +++ b/test/SILGen/sendable_to_any_for_generic_arguments.swift @@ -157,9 +157,9 @@ struct TestGeneral { } // CHECK-LABEL: sil hidden [ossa] @$s37sendable_to_any_for_generic_arguments11TestGeneralV023test_function_types_as_E5_argsyyF - // CHECK: [[FUNCV_REF:%.*]] = struct_extract %0 : $TestGeneral, #TestGeneral.funcV - // CHECK-NEXT: %3 = unchecked_trivial_bit_cast [[FUNCV_REF]] : $S<(Array) -> Optional> to $S<(Array) -> Optional> - // CHECK: [[DATA_REF:%.*]] = struct_extract %20 : $Test, #Test.data + // CHECK: [[FUNCV_REF:%.*]] = struct_extract {{%.*}} : $TestGeneral, #TestGeneral.funcV + // CHECK-NEXT: {{%.*}} = unchecked_trivial_bit_cast [[FUNCV_REF]] : $S<(Array) -> Optional> to $S<(Array) -> Optional> + // CHECK: [[DATA_REF:%.*]] = struct_extract {{%.*}} : $Test, #Test.data // CHECK-NEXT: [[DATA_COPY:%.*]] = copy_value [[DATA_REF]] // CHECK-NEXT: [[DATA_ANY:%.*]] = unchecked_value_cast [[DATA_COPY]] : $V<(Array) -> any Sendable> to $V<(Array) -> Any> // CHECK: [[ACCEPTS_ANY:%.*]] = function_ref @$s37sendable_to_any_for_generic_arguments11TestGeneralV023test_function_types_as_E5_argsyyF08accepts_C0L_yyAcDyyF1VL_VyypSayypGcGF : $@convention(thin) (@guaranteed V<(Array) -> Any>) -> () @@ -215,7 +215,7 @@ func test_subscript_computed_property_and_mutating_access(u: User) { // CHECK-NEXT: [[COPIED_ANY_DICT:%.*]] = copy_value [[ANY_LOADED_DICT]] // CHECK-NEXT: store [[COPIED_ANY_DICT]] to [init] [[ANY_DICT]] // CHECK: [[SUBSCRIPT_SETTER:%.*]] = function_ref @$sSD37sendable_to_any_for_generic_argumentsSSRszypRs_rlE5entryqd__Sgqd___tcluis - // CHECK-NEXT: %48 = apply [[SUBSCRIPT_SETTER]]({{.*}}, [[ANY_DICT]]) + // CHECK-NEXT: %{{.*}} = apply [[SUBSCRIPT_SETTER]]({{.*}}, [[ANY_DICT]]) // CHECK-NEXT: [[LOADED_ANY_DICT:%.*]] = load [take] [[ANY_DICT]] // CHECK-NEXT: [[SENDABLE_DICT:%.*]] = unchecked_bitwise_cast [[LOADED_ANY_DICT]] : $Dictionary to $Dictionary // CHECK-NEXT: [[COPIED_SENDABLE_DICT:%.*]] = copy_value [[SENDABLE_DICT]] @@ -254,7 +254,7 @@ func test_subscript_computed_property_and_mutating_access(u: User) { // CHECK-NEXT: [[COPIED_DICT:%.*]] = copy_value [[CASTED_DICT]] // CHECK-NEXT: store [[COPIED_DICT]] to [init] [[ANY_DICT]] // CHECK: [[MUTATING_METHOD:%.*]] = function_ref @$sSD37sendable_to_any_for_generic_argumentsSSRszypRs_rlE12testMutatingyyF : $@convention(method) (@inout Dictionary) -> () - // CHECK-NEXT: %101 = apply [[MUTATING_METHOD]]([[ANY_DICT]]) : $@convention(method) (@inout Dictionary) -> () + // CHECK-NEXT: {{%.*}} = apply [[MUTATING_METHOD]]([[ANY_DICT]]) : $@convention(method) (@inout Dictionary) -> () // CHECK-NEXT: [[LOADED_ANY_DICT:%.*]] = load [take] [[ANY_DICT]] // CHECK-NEXT: [[SENDABLE_DICT:%.*]] = unchecked_bitwise_cast [[LOADED_ANY_DICT]] : $Dictionary to $Dictionary // CHECK-NEXT: [[COPIED_SENDABLE_DICT:%.*]] = copy_value [[SENDABLE_DICT]] diff --git a/test/SILGen/statements.swift b/test/SILGen/statements.swift index 06aa14c814e9c..8ce32e2614d07 100644 --- a/test/SILGen/statements.swift +++ b/test/SILGen/statements.swift @@ -595,6 +595,7 @@ func testRequireOptional1(_ a : Int?) -> Int { // CHECK-NEXT: // function_ref statements.abort() -> Swift.Never // CHECK-NEXT: [[FUNC_REF:%.*]] = function_ref @$s10statements5aborts5NeverOyF // CHECK-NEXT: apply [[FUNC_REF]]() : $@convention(thin) () -> Never + // CHECK-NEXT: ignored_use // CHECK-NEXT: unreachable return t } @@ -620,6 +621,7 @@ func testRequireOptional2(_ a : String?) -> String { // CHECK-NEXT: // function_ref statements.abort() -> Swift.Never // CHECK-NEXT: [[ABORT_FUNC:%.*]] = function_ref @$s10statements5aborts5NeverOyF // CHECK-NEXT: [[NEVER:%.*]] = apply [[ABORT_FUNC]]() + // CHECK-NEXT: ignored_use // CHECK-NEXT: unreachable return t } @@ -776,6 +778,7 @@ func let_else_tuple_binding(_ a : (Int, Int)?) -> Int { // CHECK-NEXT: debug_value [[MV_1]] : $Int, let, name "x" // CHECK-NEXT: [[MV_2:%.*]] = move_value [var_decl] [[PAYLOAD_2]] : $Int // CHECK-NEXT: debug_value [[MV_2]] : $Int, let, name "y" + // CHECK-NEXT: ignored_use // CHECK-NEXT: extend_lifetime [[MV_2]] : $Int // CHECK-NEXT: extend_lifetime [[MV_1]] : $Int // CHECK-NEXT: return [[MV_1]] : $Int diff --git a/test/SILGen/switch.swift b/test/SILGen/switch.swift index 6a70eef490355..ae595a459beb8 100644 --- a/test/SILGen/switch.swift +++ b/test/SILGen/switch.swift @@ -1254,6 +1254,7 @@ func testUninhabitedSwitchScrutinee() { func test5() { // CHECK: %0 = function_ref @$s6switch12myFatalErrorAA7MyNeverOyF : $@convention(thin) () -> MyNever // CHECK-NEXT: %1 = apply %0() : $@convention(thin) () -> MyNever + // CHECK-NEXT: ignored_use %1 // CHECK-NEXT: unreachable switch myFatalError() {} } diff --git a/test/SILGen/variadic-generic-results.swift b/test/SILGen/variadic-generic-results.swift index e6492f5b2e1fa..804f35cc7fb83 100644 --- a/test/SILGen/variadic-generic-results.swift +++ b/test/SILGen/variadic-generic-results.swift @@ -126,6 +126,7 @@ func copyOrThrowIntoTuple(_ args: repeat each T) throws -> (repeat each // CHECK-NEXT: [[MV:%.*]] = move_value [var_decl] %39 : $Int // CHECK-NEXT: debug_value [[MV]] : $Int // CHECK-NEXT: [[B:%.*]] = load [take] [[R1]] : $*String +// CHECK-NEXT: ignored_use [[B]] // CHECK-NEXT: [[C:%.*]] = load [take] [[R2]] : $*String // CHECK-NEXT: [[C_LIFETIME:%.*]] = move_value [var_decl] [[C]] // CHECK-NEXT: debug_value [[C_LIFETIME]] : $String diff --git a/utils/sil-mode.el b/utils/sil-mode.el index ab559e01b3548..6712854fa8c05 100644 --- a/utils/sil-mode.el +++ b/utils/sil-mode.el @@ -198,6 +198,11 @@ "dynamic_pack_index" "pack_pack_index" "scalar_pack_index" "dealloc_pack" "dealloc_pack_metadata") 'words) . font-lock-keyword-face) + + ;; Misc uses + `(,(regexp-opt '("ignored_use") + 'words) . font-lock-keyword-face) + ;; SIL Value '("\\b[%][A-Za-z_0-9]+\\([#][0-9]+\\)?\\b" . font-lock-variable-name-face) ;; Variables From eeec27ea8664097e61438961652e24d7782e4172 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 8 Dec 2024 13:06:59 -0800 Subject: [PATCH 3/7] [rbi] Change Region Based Isolation for closures to not use the AST and instead just use SIL. The reason why I am doing this is that in certain cases the AST captures indices will never actually line up with partial apply capture indices since we seem to "smush" together closures and locally defined functions. NOTE: The reason for the really small amount of test changes is that this change does not change the actual output by design. The only cases I had to change were a case where we began to emit a better diagnostic and also where I added code coverage around _ and let _ since those require ignored_use to be implemented so that they would be diagnosed (previously we just did not emit anything so we couldn't emit the diagnostic at the SIL level). rdar://142661388 (cherry picked from commit 082b824a8efcf5b5bff00a30d2f5e5f09f3a87eb) --- include/swift/AST/DiagnosticsSIL.def | 6 + include/swift/SIL/InstructionUtils.h | 5 + .../swift/SILOptimizer/Utils/PartitionUtils.h | 6 + lib/SIL/Utils/InstructionUtils.cpp | 13 + .../Mandatory/SendNonSendable.cpp | 458 ++++++++++++------ .../Concurrency/sendable_preconcurrency.swift | 4 +- .../sendable_without_preconcurrency.swift | 8 +- .../sendable_without_preconcurrency_2.swift | 25 +- ...e_closureliterals_isolationinference.swift | 20 +- .../transfernonsendable_sending_params.swift | 35 ++ 10 files changed, 428 insertions(+), 152 deletions(-) diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 9866673dd2ed3..fce59755db1af 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -1054,6 +1054,12 @@ NOTE(regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_value_ NOTE(regionbasedisolation_typed_tns_passed_to_sending_closure_helper_multiple_value, none, "closure captures non-Sendable %0", (DeclName)) +NOTE(regionbasedisolation_closure_captures, none, + "closure captures %0", + (DeclName)) +NOTE(regionbasedisolation_closure_captures_actor, none, + "closure captures %0 allowing access to %1 state within the closure", + (DeclName, StringRef)) NOTE(regionbasedisolation_typed_tns_passed_to_sending_callee, none, "Passing %0 value of non-Sendable type %1 as a 'sending' parameter to %2 %3 risks causing races inbetween %0 uses and uses reachable from %3", diff --git a/include/swift/SIL/InstructionUtils.h b/include/swift/SIL/InstructionUtils.h index 642378ce14c64..a1ec3ea2fbbc4 100644 --- a/include/swift/SIL/InstructionUtils.h +++ b/include/swift/SIL/InstructionUtils.h @@ -129,6 +129,11 @@ bool isEndOfScopeMarker(SILInstruction *user); /// only used in recognizable patterns without otherwise "escaping". bool isIncidentalUse(SILInstruction *user); +/// Returns true if this is a move only wrapper use. +/// +/// E.x.: moveonlywrapper_to_copyable_addr, copyable_to_moveonlywrapper_value +bool isMoveOnlyWrapperUse(SILInstruction *user); + /// Return true if the given `user` instruction modifies the value's refcount /// without propagating the value or having any other effect aside from /// potentially destroying the value itself (and executing associated cleanups). diff --git a/include/swift/SILOptimizer/Utils/PartitionUtils.h b/include/swift/SILOptimizer/Utils/PartitionUtils.h index 0bbf2a2bb9c3b..b6e7423fb3249 100644 --- a/include/swift/SILOptimizer/Utils/PartitionUtils.h +++ b/include/swift/SILOptimizer/Utils/PartitionUtils.h @@ -1292,6 +1292,12 @@ struct PartitionOpEvaluator { Region sentRegion = p.getRegion(sentElement); bool isClosureCapturedElt = false; SILDynamicMergedIsolationInfo sentRegionIsolation; + + // TODO: Today we only return the first element in our region that has + // some form of isolation. This causes us to in the case of sending + // partial_applies to only emit a diagnostic for the first element in the + // capture list of the partial_apply. If we returned a list of potential + // errors... we could emit the error for each capture individually. auto pairOpt = getIsolationRegionInfo(sentRegion, op.getSourceOp()); if (!pairOpt) { return handleError(UnknownCodePatternError(op)); diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 35452d7bec3e8..52d7bb805a673 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -338,6 +338,19 @@ bool swift::isIncidentalUse(SILInstruction *user) { isa(user); } +bool swift::isMoveOnlyWrapperUse(SILInstruction *user) { + switch (user->getKind()) { + case SILInstructionKind::MoveOnlyWrapperToCopyableValueInst: + case SILInstructionKind::MoveOnlyWrapperToCopyableBoxInst: + case SILInstructionKind::MoveOnlyWrapperToCopyableAddrInst: + case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst: + case SILInstructionKind::CopyableToMoveOnlyWrapperAddrInst: + return true; + default: + return false; + } +} + bool swift::onlyAffectsRefCount(SILInstruction *user) { switch (user->getKind()) { default: diff --git a/lib/SILOptimizer/Mandatory/SendNonSendable.cpp b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp index 1ea546bfbac15..51115d3de51bb 100644 --- a/lib/SILOptimizer/Mandatory/SendNonSendable.cpp +++ b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp @@ -87,31 +87,30 @@ static SILValue stripFunctionConversions(SILValue val) { continue; } + // Look through thunks. + if (auto pai = dyn_cast(val)) { + if (pai->getCalleeFunction()->isThunk()) { + val = pai->getArgument(0); + continue; + } + } + break; } return val; } -static std::optional -getDiagnosticBehaviorLimitForCapturedValue(SILFunction *fn, - CapturedValue value) { - ValueDecl *decl = value.getDecl(); - auto *ctx = decl->getInnermostDeclContext(); - auto type = fn->mapTypeIntoContext(decl->getInterfaceType()); - return type->getConcurrencyDiagnosticBehaviorLimit(ctx); -} - /// Find the most conservative diagnostic behavior by taking the max over all /// DiagnosticBehavior for the captured values. static std::optional -getDiagnosticBehaviorLimitForCapturedValues( - SILFunction *fn, ArrayRef capturedValues) { +getDiagnosticBehaviorLimitForOperands(SILFunction *fn, + ArrayRef capturedValues) { std::optional diagnosticBehavior; for (auto value : capturedValues) { auto lhs = diagnosticBehavior.value_or(DiagnosticBehavior::Unspecified); - auto rhs = getDiagnosticBehaviorLimitForCapturedValue(fn, value).value_or( - DiagnosticBehavior::Unspecified); + auto limit = value->get()->getType().getConcurrencyDiagnosticBehavior(fn); + auto rhs = limit.value_or(DiagnosticBehavior::Unspecified); auto result = lhs.merge(rhs); if (result != DiagnosticBehavior::Unspecified) diagnosticBehavior = result; @@ -217,6 +216,103 @@ inferNameAndRootHelper(SILValue value) { return VariableNameInferrer::inferNameAndRoot(value); } +/// Find a use corresponding to the potentially recursive capture of \p +/// initialOperand that would be appropriate for diagnostics. +/// +/// \returns the use and the function argument that is used. We return the +/// function argument since it is a clever way to correctly grab the name of the +/// captured value since the ValueDecl will point at the actual ValueDecl in the +/// AST that is captured. +static std::optional> +findClosureUse(Operand *initialOperand) { + // We have to use a small vector worklist here since we are iterating through + // uses from different functions. + llvm::SmallVector, 64> worklist; + llvm::SmallPtrSet visitedOperand; + + // Initialize our worklist with uses in the initial closure. We do not want to + // analyze uses in the original function. + { + auto as = ApplySite::isa(initialOperand->getUser()); + if (!as) + return {}; + + auto *f = as.getCalleeFunction(); + if (!f) + return {}; + + unsigned argumentIndex = as.getCalleeArgIndex(*initialOperand); + auto *arg = f->getArgument(argumentIndex); + for (auto *use : arg->getUses()) { + worklist.emplace_back(use, arg); + visitedOperand.insert(use); + } + } + + while (!worklist.empty()) { + auto pair = worklist.pop_back_val(); + auto *op = pair.first; + auto *fArg = pair.second; + auto *user = op->getUser(); + + // Ignore incidental uses that are not specifically ignored use. We want to + // visit those since they represent `let _ = $VAR` and `_ = $VAR` + if (isIncidentalUse(user) && !isa(user)) + continue; + + // Look through some insts we do not care about. + if (isa( + user) || + isMoveOnlyWrapperUse(user) || + // We want to treat move_value [var_decl] as a real use since we are + // assigning to a var. + (isa(user) && + !cast(user)->isFromVarDecl())) { + for (auto result : user->getResults()) { + for (auto *use : result->getUses()) { + if (visitedOperand.insert(use).second) + worklist.emplace_back(use, fArg); + } + } + continue; + } + + // See if we have a callee function. In such a case, find our operand in the + // callee and visit its uses. + if (auto as = dyn_cast(op->getUser())) { + if (auto *f = as->getCalleeFunction()) { + auto *fArg = f->getArgument(ApplySite(as).getCalleeArgIndex(*op)); + for (auto *use : fArg->getUses()) { + if (visitedOperand.insert(use).second) + worklist.emplace_back(use, fArg); + } + continue; + } + } + + // See if we have a full apply site that was from a closure that was + // immediately invoked. In such a case, we can emit a better diagnostic in + // the called closure. + if (auto fas = FullApplySite::isa(op->getUser())) { + if (auto *f = fas.getCalleeFunction(); + f && f->getDeclRef().getClosureExpr()) { + auto *fArg = f->getArgument(fas.getCalleeArgIndex(*op)); + for (auto *use : fArg->getUses()) { + if (visitedOperand.insert(use).second) + worklist.emplace_back(use, fArg); + } + continue; + } + } + + // Otherwise, we have a real use. Return it and the function argument that + // it was derived from. + return pair; + } + + return {}; +} + //===----------------------------------------------------------------------===// // MARK: Diagnostics //===----------------------------------------------------------------------===// @@ -875,33 +971,28 @@ class UseAfterSendDiagnosticInferrer { bool UseAfterSendDiagnosticInferrer::initForIsolatedPartialApply( Operand *op, AbstractClosureExpr *ace) { - SmallVector, 8> - foundCapturedIsolationCrossing; - ace->getIsolationCrossing(foundCapturedIsolationCrossing); - if (foundCapturedIsolationCrossing.empty()) + auto diagnosticPair = findClosureUse(op); + if (!diagnosticPair) { return false; + } - unsigned opIndex = ApplySite(op->getUser()).getASTAppliedArgIndex(*op); - bool emittedDiagnostic = false; - for (auto &p : foundCapturedIsolationCrossing) { - if (std::get<1>(p) != opIndex) - continue; - emittedDiagnostic = true; + auto *diagnosticOp = diagnosticPair->first; - auto &state = sendingOpToStateMap.get(sendingOp); - if (auto rootValueAndName = inferNameAndRootHelper(sendingOp->get())) { - diagnosticEmitter.emitNamedIsolationCrossingDueToCapture( - RegularLocation(std::get<0>(p).getLoc()), rootValueAndName->first, - state.isolationInfo.getIsolationInfo(), std::get<2>(p)); - continue; - } + ApplyIsolationCrossing crossing( + *op->getFunction()->getActorIsolation(), + *diagnosticOp->getFunction()->getActorIsolation()); - diagnosticEmitter.emitTypedIsolationCrossingDueToCapture( - RegularLocation(std::get<0>(p).getLoc()), baseInferredType, - std::get<2>(p)); + auto &state = sendingOpToStateMap.get(sendingOp); + if (auto rootValueAndName = inferNameAndRootHelper(sendingOp->get())) { + diagnosticEmitter.emitNamedIsolationCrossingDueToCapture( + diagnosticOp->getUser()->getLoc(), rootValueAndName->first, + state.isolationInfo.getIsolationInfo(), crossing); + return true; } - return emittedDiagnostic; + diagnosticEmitter.emitTypedIsolationCrossingDueToCapture( + diagnosticOp->getUser()->getLoc(), baseInferredType, crossing); + return true; } void UseAfterSendDiagnosticInferrer::initForApply(Operand *op, @@ -1347,45 +1438,69 @@ class SendNeverSentDiagnosticEmitter { } } - /// Only use if we were able to find the actual isolated value. - void emitTypedSendingNeverSendableToSendingClosureParamDirectlyIsolated( - SILLocation loc, CapturedValue capturedValue) { + /// Emit an error for a case where we have captured a value like an actor and + /// thus a sending closure has become actor isolated (and thus unable to be + /// sent). + void emitClosureErrorWithCapturedActor(Operand *partialApplyOp, + Operand *actualUse, + SILArgument *fArg) { SmallString<64> descriptiveKindStr; { llvm::raw_svector_ostream os(descriptiveKindStr); - if (getIsolationRegionInfo().getIsolationInfo().isTaskIsolated()) { - os << "code in the current task"; - } else { - getIsolationRegionInfo().printForDiagnostics(os); - os << " code"; - } + getIsolationRegionInfo().getIsolationInfo().printForCodeDiagnostic(os); } - diagnoseError(loc, + diagnoseError(partialApplyOp, diag::regionbasedisolation_typed_tns_passed_sending_closure, descriptiveKindStr) - .highlight(loc.getSourceRange()) - .limitBehaviorIf(getDiagnosticBehaviorLimitForCapturedValue( - getFunction(), capturedValue)); + .limitBehaviorIf(getDiagnosticBehaviorLimitForOperands( + actualUse->getFunction(), {actualUse})); + + descriptiveKindStr.clear(); + { + llvm::raw_svector_ostream os(descriptiveKindStr); + getIsolationRegionInfo().getIsolationInfo().printForDiagnostics(os); + } + diagnoseNote(actualUse, diag::regionbasedisolation_closure_captures_actor, + fArg->getDecl()->getName(), descriptiveKindStr); + } - auto capturedLoc = RegularLocation(capturedValue.getLoc()); + /// Emit a typed error for an isolated closure being passed as a sending + /// parameter. + /// + /// \arg partialApplyOp the operand of the outermost partial apply. + /// \arg actualUse the operand inside the closure that actually caused the + /// capture to occur. This maybe inside a different function from the partial + /// apply since we want to support a use inside a recursive closure. + void emitSendingClosureParamDirectlyIsolated(Operand *partialApplyOp, + Operand *actualUse, + SILArgument *fArg) { + SmallString<64> descriptiveKindStr; + { + llvm::raw_svector_ostream os(descriptiveKindStr); + getIsolationRegionInfo().getIsolationInfo().printForCodeDiagnostic(os); + } + + diagnoseError(partialApplyOp, + diag::regionbasedisolation_typed_tns_passed_sending_closure, + descriptiveKindStr) + .limitBehaviorIf(getDiagnosticBehaviorLimitForOperands( + actualUse->getFunction(), {actualUse})); + + // If we have a closure capture box, emit a special diagnostic. if (getIsolationRegionInfo().getIsolationInfo().isTaskIsolated()) { - // If we have a closure capture box, emit a special diagnostic. - if (auto *fArg = dyn_cast( - getIsolationRegionInfo().getIsolationInfo().getIsolatedValue())) { - if (fArg->isClosureCapture() && fArg->getType().is()) { - auto diag = diag:: - regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_boxed_value_task_isolated; - auto *decl = capturedValue.getDecl(); - diagnoseNote(capturedLoc, diag, decl->getName(), - decl->getDescriptiveKind()); - return; - } + if (cast(fArg)->isClosureCapture() && + fArg->getType().is()) { + auto diag = diag:: + regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_boxed_value_task_isolated; + diagnoseNote(actualUse, diag, fArg->getDecl()->getName(), + fArg->getDecl()->getDescriptiveKind()); + return; } auto diag = diag:: regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_value_task_isolated; - diagnoseNote(capturedLoc, diag, capturedValue.getDecl()->getName()); + diagnoseNote(actualUse, diag, fArg->getDecl()->getName()); return; } @@ -1397,41 +1512,55 @@ class SendNeverSentDiagnosticEmitter { auto diag = diag:: regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_value; - diagnoseNote(capturedLoc, diag, descriptiveKindStr, - capturedValue.getDecl()->getName()); + diagnoseNote(actualUse, diag, descriptiveKindStr, + fArg->getDecl()->getName()); } - void emitTypedSendingNeverSendableToSendingClosureParam( - SILLocation loc, ArrayRef capturedValues) { + void emitSendingClosureMultipleCapturedOperandError( + SILLocation loc, ArrayRef capturedOperands) { + // Our caller should have passed at least one operand. Emit an unknown error + // to signal we need a bug report. + if (capturedOperands.empty()) { + emitUnknownPatternError(); + return; + } + SmallString<64> descriptiveKindStr; { llvm::raw_svector_ostream os(descriptiveKindStr); - if (getIsolationRegionInfo().getIsolationInfo().isTaskIsolated()) { - os << "code in the current task"; - } else { - getIsolationRegionInfo().printForDiagnostics(os); - os << " code"; + getIsolationRegionInfo()->printForCodeDiagnostic(os); + } + + auto emitMainError = [&] { + auto behaviorLimit = getDiagnosticBehaviorLimitForOperands( + getFunction(), capturedOperands); + diagnoseError(loc, + diag::regionbasedisolation_typed_tns_passed_sending_closure, + descriptiveKindStr) + .highlight(loc.getSourceRange()) + .limitBehaviorIf(behaviorLimit); + }; + + if (capturedOperands.size() == 1) { + auto captured = capturedOperands.front(); + auto actualUseInfo = findClosureUse(captured); + + // If we fail to find actual use info, emit an unknown error. + if (!actualUseInfo) { + emitUnknownPatternError(); + return; } - } - - auto behaviorLimit = getDiagnosticBehaviorLimitForCapturedValues( - getFunction(), capturedValues); - diagnoseError(loc, - diag::regionbasedisolation_typed_tns_passed_sending_closure, - descriptiveKindStr) - .highlight(loc.getSourceRange()) - .limitBehaviorIf(behaviorLimit); - if (capturedValues.size() == 1) { - auto captured = capturedValues.front(); - auto capturedLoc = RegularLocation(captured.getLoc()); - if (getIsolationRegionInfo().getIsolationInfo().isTaskIsolated()) { + if (getIsolationRegionInfo()->isTaskIsolated()) { + emitMainError(); auto diag = diag:: regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_value_task_isolated; - diagnoseNote(capturedLoc, diag, captured.getDecl()->getName()); + diagnoseNote(actualUseInfo->first, diag, + actualUseInfo->second->getDecl()->getName()); return; } + emitMainError(); descriptiveKindStr.clear(); { llvm::raw_svector_ostream os(descriptiveKindStr); @@ -1439,16 +1568,29 @@ class SendNeverSentDiagnosticEmitter { } auto diag = diag:: regionbasedisolation_typed_tns_passed_to_sending_closure_helper_have_value_region; - diagnoseNote(capturedLoc, diag, descriptiveKindStr, - captured.getDecl()->getName()); + diagnoseNote(actualUseInfo->first, diag, descriptiveKindStr, + actualUseInfo->second->getDecl()->getName()); return; } - for (auto captured : capturedValues) { - auto capturedLoc = RegularLocation(captured.getLoc()); + emitMainError(); + + bool emittedDiagnostic = false; + for (auto captured : capturedOperands) { + auto actualUseInfo = findClosureUse(captured); + if (!actualUseInfo) + continue; + emittedDiagnostic = true; auto diag = diag:: regionbasedisolation_typed_tns_passed_to_sending_closure_helper_multiple_value; - diagnoseNote(capturedLoc, diag, captured.getDecl()->getName()); + diagnoseNote(actualUseInfo->first, diag, + actualUseInfo->second->getDecl()->getName()); + } + + // Check if we did not emit a diagnostic. In such a case, we need to emit an + // unknown patten error so that we get a bug report from the user. + if (!emittedDiagnostic) { + emitUnknownPatternError(); } } @@ -1563,6 +1705,12 @@ class SendNeverSentDiagnosticEmitter { return diagnoseError(inst->getLoc(), diag, std::forward(args)...); } + template + InFlightDiagnostic diagnoseError(Operand *op, Diag diag, U &&...args) { + return diagnoseError(op->getUser()->getLoc(), diag, + std::forward(args)...); + } + template InFlightDiagnostic diagnoseNote(SourceLoc loc, Diag diag, U &&...args) { return getASTContext().Diags.diagnose(loc, diag, std::forward(args)...); @@ -1579,6 +1727,12 @@ class SendNeverSentDiagnosticEmitter { U &&...args) { return diagnoseNote(inst->getLoc(), diag, std::forward(args)...); } + + template + InFlightDiagnostic diagnoseNote(Operand *op, Diag diag, U &&...args) { + return diagnoseNote(op->getUser()->getLoc(), diag, + std::forward(args)...); + } }; class SentNeverSendableDiagnosticInferrer { @@ -1629,67 +1783,101 @@ class SentNeverSendableDiagnosticInferrer { } // namespace bool SentNeverSendableDiagnosticInferrer::initForSendingPartialApply( - FullApplySite fas, Operand *paiOp) { - auto *pai = - dyn_cast(stripFunctionConversions(paiOp->get())); - if (!pai) + FullApplySite fas, Operand *callsiteOp) { + // This is the partial apply that is being passed as a sending parameter. + auto *sendingPAI = + dyn_cast(stripFunctionConversions(callsiteOp->get())); + if (!sendingPAI) return false; - // For now we want this to be really narrow and to only apply to closure - // literals. - auto *ce = pai->getLoc().getAsASTNode(); - if (!ce) + // Make sure that we only handle closure literals. + // + // TODO: This should be marked on closures at the SIL level... I shouldn't + // have to refer to the AST. + if (!sendingPAI->getLoc().getAsASTNode()) return false; - // Ok, we now know we have a partial apply and it is a closure literal. Lets - // see if we can find the exact thing that caused the closure literal to be - // actor isolated. - auto isolationInfo = diagnosticEmitter.getIsolationRegionInfo(); - if (isolationInfo->hasIsolatedValue()) { - // Now that we have the value, see if that value is one of our captured - // values. - auto isolatedValue = isolationInfo->getIsolatedValue(); - auto matchingElt = getIsolatedValuePartialApplyIndex(pai, isolatedValue); - if (matchingElt) { - // Ok, we found the matching element. Lets emit our diagnostic! - auto capture = ce->getCaptureInfo().getCaptures()[*matchingElt]; - diagnosticEmitter - .emitTypedSendingNeverSendableToSendingClosureParamDirectlyIsolated( - ce, capture); + // Ok, we have a closure literal. First we handle a potential capture of + // 'self' before we do anything by looping over our captured parameters. + // + // DISCUSSION: The reason why we do this early is that as a later heuristic, + // we check if any of the values are directly task isolated (i.e. they are + // actually the task isolated value, not a value that is in the same region as + // something that is task isolated). This could potentially result in us + // emitting a task isolated error instead of an actor isolated error here if + // for some reason SILGen makes the self capture come later in the capture + // list. From a compile time perspective, going over a list of captures twice + // is not going to hurt especially since we are going to emit a diagnostic + // here anyways. + for (auto &sendingPAIOp : sendingPAI->getArgumentOperands()) { + // NOTE: If we access a field on self in the closure, we will still just + // capture self... so we do not have to handle that case due to the way + // SILGen codegens today. This is also true if we use a capture list [x = + // self.field] (i.e. a closure that captures a field from self is still + // nonisolated). + if (!sendingPAIOp.get()->getType().isAnyActor()) + continue; + + auto *fArg = dyn_cast( + lookThroughOwnershipInsts(sendingPAIOp.get())); + if (!fArg || !fArg->isSelf()) + continue; + + auto capturedValue = findClosureUse(&sendingPAIOp); + if (!capturedValue) { + // If we failed to find the direct capture of self, emit an unknown code + // pattern error so the user knows to send a bug report. This should never + // fail. + diagnosticEmitter.emitUnknownPatternError(); return true; } + + // Otherwise, emit our captured actor error. + diagnosticEmitter.emitClosureErrorWithCapturedActor( + &sendingPAIOp, capturedValue->first, capturedValue->second); + return true; } - // Ok, we are not tracking an actual isolated value or we do not capture the - // isolated value directly... we need to be smarter here. First lets gather up - // all non-Sendable values captured by the closure. - SmallVector nonSendableCaptures; - for (auto capture : ce->getCaptureInfo().getCaptures()) { - auto *decl = capture.getDecl(); - auto type = decl->getInterfaceType()->getCanonicalType(); - auto silType = SILType::getPrimitiveObjectType(type); - if (!SILIsolationInfo::isNonSendableType(silType, pai->getFunction())) + // Ok, we know that we have a closure expr. We now need to find the specific + // closure captured value that is actor or task isolated. Then we search for + // the potentially recursive closure use so we can show a nice loc to the + // user. + auto maybeIsolatedValue = + diagnosticEmitter.getIsolationRegionInfo()->maybeGetIsolatedValue(); + + // If we do not find an actual task isolated value while looping below, this + // contains the non sendable captures of the partial apply that we want to + // emit a more heuristic based error for. See documentation below. + SmallVector nonSendableOps; + + for (auto &sendingPAIOp : sendingPAI->getArgumentOperands()) { + // If our value's rep is task isolated or is the dynamic isolated + // value... then we are done. This is a 'correct' error value to emit. + auto trackableValue = valueMap.getTrackableValue(sendingPAIOp.get()); + if (trackableValue.isSendable()) continue; - auto *fromDC = decl->getInnermostDeclContext(); - auto *nom = silType.getNominalOrBoundGenericNominal(); - if (nom && fromDC) { - if (auto diagnosticBehavior = - getConcurrencyDiagnosticBehaviorLimit(nom, fromDC)) { - if (*diagnosticBehavior == DiagnosticBehavior::Ignore) - continue; + auto rep = trackableValue.getRepresentative().maybeGetValue(); + nonSendableOps.push_back(&sendingPAIOp); + + if (trackableValue.getIsolationRegionInfo().isTaskIsolated() || + rep == maybeIsolatedValue) { + if (auto capturedValue = findClosureUse(&sendingPAIOp)) { + diagnosticEmitter.emitSendingClosureParamDirectlyIsolated( + callsiteOp, capturedValue->first, capturedValue->second); + return true; } } - nonSendableCaptures.push_back(capture); } - // If we do not have any non-Sendable captures... bail. - if (nonSendableCaptures.empty()) - return false; - - // Otherwise, emit the diagnostic. - diagnosticEmitter.emitTypedSendingNeverSendableToSendingClosureParam( - ce, nonSendableCaptures); + // If we did not find a clear answer in terms of an isolated value, we emit a + // more general error based on: + // + // 1. If we have one non-Sendable value then we know that must be the value. + // 2. Otherwise, we emit a generic captured non-Sendable value error to give + // people something to work off of. + diagnosticEmitter.emitSendingClosureMultipleCapturedOperandError( + callsiteOp->getUser()->getLoc(), nonSendableOps); return true; } diff --git a/test/Concurrency/sendable_preconcurrency.swift b/test/Concurrency/sendable_preconcurrency.swift index 51a85e9ee5149..4b23171987eaf 100644 --- a/test/Concurrency/sendable_preconcurrency.swift +++ b/test/Concurrency/sendable_preconcurrency.swift @@ -32,11 +32,11 @@ struct MyType3 { func testA(ns: NS, mt: MyType, mt2: MyType2, mt3: MyType3, sc: StrictClass, nsc: NonStrictClass) async { Task { // expected-tns-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} - print(ns) + print(ns) // expected-tns-note {{closure captures 'ns' which is accessible to code in the current task}} print(mt) print(mt2) print(mt3) - print(sc) // expected-tns-note {{closure captures 'sc' which is accessible to code in the current task}} + print(sc) print(nsc) } } diff --git a/test/Concurrency/sendable_without_preconcurrency.swift b/test/Concurrency/sendable_without_preconcurrency.swift index 3c26a58919a8c..18ba37f09770f 100644 --- a/test/Concurrency/sendable_without_preconcurrency.swift +++ b/test/Concurrency/sendable_without_preconcurrency.swift @@ -27,10 +27,10 @@ struct MyType2 { func testA(ns: NS, mt: MyType, mt2: MyType2, sc: StrictClass, nsc: NonStrictClass) async { Task { // expected-tns-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} - print(ns) // expected-tns-note {{closure captures non-Sendable 'ns'}} - print(mt) // expected-tns-note {{closure captures non-Sendable 'mt'}} - print(mt2) // expected-tns-note {{closure captures non-Sendable 'mt2'}} - print(sc) // expected-tns-note {{closure captures non-Sendable 'sc'}} + print(ns) // expected-tns-note {{closure captures 'ns' which is accessible to code in the current task}} + print(mt) + print(mt2) + print(sc) } } diff --git a/test/Concurrency/sendable_without_preconcurrency_2.swift b/test/Concurrency/sendable_without_preconcurrency_2.swift index 3126e1c70f3ed..35ddfcbe4da36 100644 --- a/test/Concurrency/sendable_without_preconcurrency_2.swift +++ b/test/Concurrency/sendable_without_preconcurrency_2.swift @@ -30,11 +30,30 @@ struct MyType2: Sendable { func testA(ns: NS, mt: MyType, mt2: MyType2, sc: StrictClass, nsc: NonStrictClass) async { Task { // expected-tns-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} - print(ns) - print(mt) // no warning with targeted: MyType is Sendable because we suppressed NonStrictClass's warning + print(ns) // expected-tns-note {{closure captures 'ns' which is accessible to code in the current task}} + print(mt) print(mt2) print(sc) - print(nsc) // expected-tns-note {{closure captures 'nsc' which is accessible to code in the current task}} + print(nsc) + } +} + +// No warning with targeted: MyType is Sendable because we suppressed NonStrictClass's warning. +func testB(mt: MyType) async { + Task { // expected-tns-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} + print(mt) // expected-tns-note {{closure captures 'mt' which is accessible to code in the current task}} + } +} + +func testNonStrictClass(_ mt: NonStrictClass) async { + Task { // expected-tns-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} + print(mt) // expected-tns-note {{closure captures 'mt' which is accessible to code in the current task}} + } +} + +func testStrictClass(_ mt: StrictClass) async { + Task { // expected-tns-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} + print(mt) // expected-tns-note {{closure captures 'mt' which is accessible to code in the current task}} } } diff --git a/test/Concurrency/transfernonsendable_closureliterals_isolationinference.swift b/test/Concurrency/transfernonsendable_closureliterals_isolationinference.swift index a59f706799a45..e073badfbf2ac 100644 --- a/test/Concurrency/transfernonsendable_closureliterals_isolationinference.swift +++ b/test/Concurrency/transfernonsendable_closureliterals_isolationinference.swift @@ -475,8 +475,9 @@ extension MyActor { // // CHECK-LABEL: // closure #2 in MyActor.test_CallerSyncInheritsActorContext_CalleeSyncNonisolated() // CHECK-NEXT: // Isolation: actor_instance. name: 'self' - inheritActorContextAcceptsSendingClosure { print(self) } // expected-error {{sending value of non-Sendable type '() -> ()' risks causing data races}} - // expected-note @-1 {{Passing 'self'-isolated value of non-Sendable type '() -> ()' as a 'sending' parameter to global function 'inheritActorContextAcceptsSendingClosure' risks causing races inbetween 'self'-isolated uses and uses reachable from 'inheritActorContextAcceptsSendingClosure'}} + inheritActorContextAcceptsSendingClosure { // expected-error {{passing closure as a 'sending' parameter risks causing data races between 'self'-isolated code and concurrent execution of the closure}} + print(self) // expected-note {{closure captures 'self'}} + } // CHECK-LABEL: // closure #3 in MyActor.test_CallerSyncInheritsActorContext_CalleeSyncNonisolated() // CHECK-NEXT: // Isolation: actor_instance. name: 'self' @@ -512,8 +513,9 @@ extension MyActor { // // CHECK-LABEL: // closure #2 in MyActor.test_CallerSyncInheritsActorContext_CalleeSyncMainActorIsolated() // CHECK-NEXT: // Isolation: actor_instance. name: 'self' - await inheritActorContextGlobalActorAcceptsSendingClosure { print(self) } // expected-error {{sending value of non-Sendable type '() -> ()' risks causing data races}} - // expected-note @-1 {{Passing 'self'-isolated value of non-Sendable type '() -> ()' as a 'sending' parameter to global function 'inheritActorContextGlobalActorAcceptsSendingClosure' risks causing races inbetween 'self'-isolated uses and uses reachable from 'inheritActorContextGlobalActorAcceptsSendingClosure'}} + await inheritActorContextGlobalActorAcceptsSendingClosure { // expected-error {{passing closure as a 'sending' parameter risks causing data races between 'self'-isolated code and concurrent execution of the closure}} + print(self) // expected-note {{closure captures 'self'}} + } // CHECK-LABEL: // closure #3 in MyActor.test_CallerSyncInheritsActorContext_CalleeSyncMainActorIsolated() // CHECK-NEXT: // Isolation: actor_instance. name: 'self' @@ -630,8 +632,9 @@ extension MyActor { // // CHECK-LABEL: // closure #2 in MyActor.test_CallerAsyncInheritsActorContext_CalleeSyncNonisolated() // CHECK-NEXT: // Isolation: actor_instance. name: 'self' - await asyncInheritActorContextAcceptsSendingClosure { print(self) } // expected-error {{sending value of non-Sendable type '() -> ()' risks causing data races}} - // expected-note @-1 {{Passing 'self'-isolated value of non-Sendable type '() -> ()' as a 'sending' parameter to global function 'asyncInheritActorContextAcceptsSendingClosure' risks causing races inbetween 'self'-isolated uses and uses reachable from 'asyncInheritActorContextAcceptsSendingClosure'}} + await asyncInheritActorContextAcceptsSendingClosure { // expected-error {{passing closure as a 'sending' parameter risks causing data races between 'self'-isolated code and concurrent execution of the closure}} + print(self) // expected-note {{closure captures 'self'}} + } // CHECK-LABEL: // closure #3 in MyActor.test_CallerAsyncInheritsActorContext_CalleeSyncNonisolated() // CHECK-NEXT: // Isolation: actor_instance. name: 'self' @@ -667,8 +670,9 @@ extension MyActor { // // CHECK-LABEL: // closure #2 in MyActor.test_CallerAsyncInheritsActorContext_CalleeSyncMainActorIsolated() // CHECK-NEXT: // Isolation: actor_instance. name: 'self' - await asyncInheritActorContextGlobalActorAcceptsSendingClosure { print(self) } // expected-error {{sending value of non-Sendable type '() -> ()' risks causing data races}} - // expected-note @-1 {{Passing 'self'-isolated value of non-Sendable type '() -> ()' as a 'sending' parameter to global function 'asyncInheritActorContextGlobalActorAcceptsSendingClosure' risks causing races inbetween 'self'-isolated uses and uses reachable from 'asyncInheritActorContextGlobalActorAcceptsSendingClosure'}} + await asyncInheritActorContextGlobalActorAcceptsSendingClosure { // expected-error {{passing closure as a 'sending' parameter risks causing data races between 'self'-isolated code and concurrent execution of the closure}} + print(self) // expected-note {{closure captures 'self'}} + } // CHECK-LABEL: // closure #3 in MyActor.test_CallerAsyncInheritsActorContext_CalleeSyncMainActorIsolated() // CHECK-NEXT: // Isolation: actor_instance. name: 'self' diff --git a/test/Concurrency/transfernonsendable_sending_params.swift b/test/Concurrency/transfernonsendable_sending_params.swift index 647a71174ffb2..88504220097ce 100644 --- a/test/Concurrency/transfernonsendable_sending_params.swift +++ b/test/Concurrency/transfernonsendable_sending_params.swift @@ -518,6 +518,12 @@ func taskIsolatedCaptureInSendingClosureLiteral(_ x: NonSendableKlass) { print(x) // expected-note {{closure captures 'x' which is accessible to code in the current task}} } + Task { // expected-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} + { + print(x) // expected-note {{closure captures 'x' which is accessible to code in the current task}} + }() + } + takeClosure { // expected-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} print(x) // expected-note {{closure captures 'x' which is accessible to code in the current task}} } @@ -617,3 +623,32 @@ func disconnectedPassedSendingToNonIsolatedCalleeIsolatedParam3( // expected-note @-1 {{'c' used after being passed as a 'sending' parameter}} c.use() // expected-note {{access can happen concurrently}} } + +// In all of the below, we don't know that 'a' is the same isolation as the +// closure isolation. +func testNonSendableCaptures(ns: NonSendableKlass, a: isolated MyActor) { + Task { + _ = a + _ = ns + } + + Task { [a] in // expected-warning {{passing closure as a 'sending' parameter risks causing data races between 'a'-isolated code and concurrent execution of the closure}} + _ = a + _ = ns // expected-note {{closure captures 'a'-isolated 'ns'}} + } + + Task { + let _ = a + let _ = ns + } + + Task { [a] in // expected-warning {{passing closure as a 'sending' parameter risks causing data races between 'a'-isolated code and concurrent execution of the closure}} + let _ = a + let _ = ns // expected-note {{closure captures 'a'-isolated 'ns'}} + } + + Task { [a] in // expected-warning {{passing closure as a 'sending' parameter risks causing data races between 'a'-isolated code and concurrent execution of the closure}} + let (_, _) = (a, ns) // expected-note {{closure captures 'a'-isolated 'ns'}} + let _ = ns + } +} From 01b2db54556df8eeda760246a794b5c83a028aaa Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 23 Jan 2025 10:58:45 -0800 Subject: [PATCH 4/7] [sil] Add conformance to UnaryInstruction for IgnoredUseInst. (cherry picked from commit 05511c955aac613fe870ec79df3cfffd2f339e7c) --- SwiftCompilerSources/Sources/SIL/Instruction.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 6a51f0d0e09b5..f11fa09e04a65 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -1616,5 +1616,5 @@ final public class ThunkInst : Instruction { final public class MergeIsolationRegionInst : Instruction { } -final public class IgnoredUseInst : Instruction { +final public class IgnoredUseInst : Instruction, UnaryInstruction { } From e17551c0dc78997a10a0f89d9ce0c379d0cc93d7 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 29 Jan 2025 15:03:57 -0800 Subject: [PATCH 5/7] [sil] Make sure that we do not flag ignored_use as dead code when used after a try_apply or begin_apply. (cherry picked from commit 39e63b488fed2c714ff94621b251dae7455864c8) --- .../Mandatory/DiagnoseUnreachable.cpp | 5 +- test/SILOptimizer/noreturn_folding.sil | 50 ++++++++++++++++--- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp index efd0b02b7c64c..7ad0318ff3a2b 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp @@ -777,8 +777,9 @@ static bool simplifyBlocksWithCallsToNoReturn(SILBasicBlock &BB, // If we have an ignored use whose operand is our no return call, ignore it. if (auto *i = dyn_cast(currInst)) { - if (auto *svi = dyn_cast(i->getOperand()); - svi && getAsCallToNoReturn(svi)) { + // This handles try_apply, apply, begin_apply. + if (auto *inst = i->getOperand()->getDefiningInstructionOrTerminator(); + inst && inst == noReturnCall) { return false; } } diff --git a/test/SILOptimizer/noreturn_folding.sil b/test/SILOptimizer/noreturn_folding.sil index 7cdbea8f657ad..17cbe138d8268 100644 --- a/test/SILOptimizer/noreturn_folding.sil +++ b/test/SILOptimizer/noreturn_folding.sil @@ -1,12 +1,13 @@ -// RUN: %target-sil-opt -module-name Swift -enable-sil-verify-all -noreturn-folding < %s | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all -noreturn-folding -verify %s | %FileCheck %s +import Swift import Builtin -enum Never {} - -struct Int64 { - var value: Builtin.Int64 -} +sil @exit : $@convention(thin) (Builtin.Int32) -> Never +sil @returnNever : $@convention(thin) () -> Never +sil @returnNeverThrows : $@convention(thin) () -> (Never, @error Error) +sil @returnNeverCoroutine : $@yield_once @convention(thin) () -> @yields Never +sil @doSomething : $@convention(thin) () -> () // We used to crash on this IR. We would delete "%4" while there is still a // (dead) user "%7" around. @@ -15,7 +16,7 @@ struct Int64 { // CHECK: %[[E:.+]] = function_ref @exit // CHECK: apply %[[E]] // CHECK: unreachable - +// CHECK: } // end sil function 'unreachable_outside_block_user' sil private @unreachable_outside_block_user : $@convention(thin) () -> Int64 { bb0: %0 = integer_literal $Builtin.Int1, -1 @@ -23,7 +24,9 @@ bb0: // function_ref exit %2 = function_ref @exit : $@convention(thin) (Builtin.Int32) -> Never %3 = apply %2(%1) : $@convention(thin) (Builtin.Int32) -> Never + // expected-note @-1 {{a call to a never-returning function}} %4 = integer_literal $Builtin.Int64, 7 + // expected-warning @-1 {{will never be executed}} cond_br %0, bb1, bb2 bb1: @@ -42,4 +45,35 @@ bb3 (%11: $Int64): return %11 : $Int64 } -sil @exit : $@convention(thin) (Builtin.Int32) -> Never +// Make sure we do not emit any error here. +sil @ignore_use_apply : $@convention(thin) () -> () { +bb0: + %0 = function_ref @returnNever : $@convention(thin) () -> Never + %1 = apply %0() : $@convention(thin) () -> Never + ignored_use %1 : $Never + unreachable +} + +// Make sure we do not emit any error here. +sil [ossa] @ignore_use_try_apply : $@convention(thin) () -> () { +bb0: + %0 = function_ref @returnNeverThrows : $@convention(thin) () -> (Never, @error Error) + try_apply %0() : $@convention(thin) () -> (Never, @error Error), normal bb1, error bb2 + +bb1(%2 : $Never): + ignored_use %2 : $Never + unreachable + +bb2(%5 : @owned $Error): + %6 = builtin "unexpectedError"(%5 : $Error) : $() + unreachable +} + +sil [ossa] @ignore_use_begin_apply : $@convention(thin) () -> () { +bb0: + %0 = function_ref @returnNeverCoroutine : $@yield_once @convention(thin) () -> @yields Never + (%1, %2) = begin_apply %0() : $@yield_once @convention(thin) () -> @yields Never + ignored_use %1 + end_apply %2 as $() + unreachable +} From 243983ffe889b675a091d41faaf6df88c109c342 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 29 Jan 2025 15:05:37 -0800 Subject: [PATCH 6/7] [rbi] When finding closure uses in an immediately invoked closure, distinguish in between captures and parameters. Otherwise, when one diagnoses code like the following: ``` Task { { use($0) }(x) } ``` One gets that $0 was captured instead of x. Unfortunately, since function parameters do not have locations associated with them, we do not mark x itself... instead, we mark the closure... which is unfortunate. (cherry picked from commit 8c96a8db1b980d9f1cd72882007db4e7f54ab4e2) --- lib/SILOptimizer/Mandatory/SendNonSendable.cpp | 16 +++++++++------- .../transfernonsendable_sending_params.swift | 6 ++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/SendNonSendable.cpp b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp index 51115d3de51bb..9b16ab3ef9e82 100644 --- a/lib/SILOptimizer/Mandatory/SendNonSendable.cpp +++ b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp @@ -294,14 +294,16 @@ findClosureUse(Operand *initialOperand) { // immediately invoked. In such a case, we can emit a better diagnostic in // the called closure. if (auto fas = FullApplySite::isa(op->getUser())) { - if (auto *f = fas.getCalleeFunction(); - f && f->getDeclRef().getClosureExpr()) { - auto *fArg = f->getArgument(fas.getCalleeArgIndex(*op)); - for (auto *use : fArg->getUses()) { - if (visitedOperand.insert(use).second) - worklist.emplace_back(use, fArg); + if (auto *f = fas.getCalleeFunction()) { + auto *fArg = cast( + f->getArgument(fas.getCalleeArgIndex(*op))); + if (fArg->isClosureCapture()) { + for (auto *use : fArg->getUses()) { + if (visitedOperand.insert(use).second) + worklist.emplace_back(use, fArg); + } + continue; } - continue; } } diff --git a/test/Concurrency/transfernonsendable_sending_params.swift b/test/Concurrency/transfernonsendable_sending_params.swift index 88504220097ce..e71857f4f2a87 100644 --- a/test/Concurrency/transfernonsendable_sending_params.swift +++ b/test/Concurrency/transfernonsendable_sending_params.swift @@ -524,6 +524,12 @@ func taskIsolatedCaptureInSendingClosureLiteral(_ x: NonSendableKlass) { }() } + Task { // expected-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} + { // expected-note {{closure captures 'x' which is accessible to code in the current task}} + print($0) + }(x) + } + takeClosure { // expected-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}} print(x) // expected-note {{closure captures 'x' which is accessible to code in the current task}} } From e99291fd072de876915ac3c5c6e03e16e1893f48 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 30 Jan 2025 13:19:53 -0800 Subject: [PATCH 7/7] Update a test for 6.1. --- test/SILOptimizer/noreturn_folding.sil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SILOptimizer/noreturn_folding.sil b/test/SILOptimizer/noreturn_folding.sil index 17cbe138d8268..9fa5d4d296f23 100644 --- a/test/SILOptimizer/noreturn_folding.sil +++ b/test/SILOptimizer/noreturn_folding.sil @@ -73,7 +73,7 @@ sil [ossa] @ignore_use_begin_apply : $@convention(thin) () -> () { bb0: %0 = function_ref @returnNeverCoroutine : $@yield_once @convention(thin) () -> @yields Never (%1, %2) = begin_apply %0() : $@yield_once @convention(thin) () -> @yields Never - ignored_use %1 + ignored_use %1 : $Never end_apply %2 as $() unreachable }