diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index a21ebf77628d9..f11fa09e04a65 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, UnaryInstruction { +} 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/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/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/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/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..52d7bb805a673 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -334,7 +334,21 @@ 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::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) { @@ -622,6 +636,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/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/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/Mandatory/DiagnoseUnreachable.cpp b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp index 874f1a9f4d256..7ad0318ff3a2b 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp @@ -775,6 +775,15 @@ 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)) { + // This handles try_apply, apply, begin_apply. + if (auto *inst = i->getOperand()->getDefiningInstructionOrTerminator(); + inst && inst == noReturnCall) { + 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/lib/SILOptimizer/Mandatory/SendNonSendable.cpp b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp index 1ea546bfbac15..9b16ab3ef9e82 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,105 @@ 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()) { + 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; + } + } + } + + // Otherwise, we have a real use. Return it and the function argument that + // it was derived from. + return pair; + } + + return {}; +} + //===----------------------------------------------------------------------===// // MARK: Diagnostics //===----------------------------------------------------------------------===// @@ -875,33 +973,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 +1440,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); + } + + /// 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); + } - auto capturedLoc = RegularLocation(capturedValue.getLoc()); + 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 +1514,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 +1570,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 +1707,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 +1729,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 +1785,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/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/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/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.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/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..e71857f4f2a87 100644 --- a/test/Concurrency/transfernonsendable_sending_params.swift +++ b/test/Concurrency/transfernonsendable_sending_params.swift @@ -518,6 +518,18 @@ 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}} + }() + } + + 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}} } @@ -617,3 +629,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 + } +} 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 : $() } + 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/test/SILOptimizer/noreturn_folding.sil b/test/SILOptimizer/noreturn_folding.sil index 7cdbea8f657ad..9fa5d4d296f23 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 : $Never + end_apply %2 as $() + unreachable +} 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