Repository: swiftlang/swift
Pull request #81542: [concurrency] Fix a few issues around not emitted correct protocol witness and vtable thunks for nonisolated(nonsending)
Author: login: gottesmm
Created: 2025-05-15T21:09:49Z | Merged: 2025-05-19T17:43:18Z

I did a few things in this PR to effectuate the title:

1. I discovered that we were not validating in the SILVerifier that protocol witness table witnesses were ABI compatible with the requirements. I added that verification. It also showed we had some stale SIL tests that were very old that just were updated incorrectly and thus tripped this verification. I fixed those.
2. I changed SILGen to place the correct ActorIsolation on protocol witness thunks. We were using the isolation from the witness, not the requirement. This ensures that we properly place the nonisolated(nonsending) parameter on the thunk as appropriate.
3. I found that we were not properly pushing/popping the implicit parameter on vtable overrides.

rdar://151394209


Diff:
diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h
index 6859c87bf37e3..a84a1e0796711 100644
--- a/include/swift/SIL/TypeLowering.h
+++ b/include/swift/SIL/TypeLowering.h
@@ -1389,6 +1389,14 @@ CanSILFunctionType getNativeSILFunctionType(
     std::optional<SubstitutionMap> reqtSubs = std::nullopt,
     ProtocolConformanceRef witnessMethodConformance = ProtocolConformanceRef());
 
+/// origConstant is the parent decl ref in the case of class methods and the
+/// witness method decl ref if we are working with a protocol witness. Pass in
+/// None otherwise.
+std::optional<ActorIsolation>
+getSILFunctionTypeActorIsolation(CanAnyFunctionType substFnInterfaceType,
+                                 std::optional<SILDeclRef> origConstant,
+                                 std::optional<SILDeclRef> constant);
+
 /// The thunk kinds used in the differentiation transform.
 enum class DifferentiationThunkKind {
   /// A reabstraction thunk.
diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp
index 9991ac4be4099..e399bdc96a189 100644
--- a/lib/SIL/IR/SILFunctionType.cpp
+++ b/lib/SIL/IR/SILFunctionType.cpp
@@ -2386,6 +2386,68 @@ static void destructureYieldsForCoroutine(TypeConverter &TC,
   }
 }
 
+std::optional<ActorIsolation>
+swift::getSILFunctionTypeActorIsolation(CanAnyFunctionType substFnInterfaceType,
+                                        std::optional<SILDeclRef> origConstant,
+                                        std::optional<SILDeclRef> constant) {
+  // If we have origConstant then we are creating a protocol method thunk. In
+  // such a case, we want to use the origConstant's actor isolation.
+  if (origConstant && constant &&
+      *origConstant != *constant) {
+    if (auto *decl = origConstant->getAbstractFunctionDecl()) {
+      if (auto *nonisolatedAttr =
+              decl->getAttrs().getAttribute<NonisolatedAttr>()) {
+        if (nonisolatedAttr->isNonSending())
+          return ActorIsolation::forCallerIsolationInheriting();
+      }
+
+      if (decl->getAttrs().hasAttribute<ConcurrentAttr>()) {
+        return ActorIsolation::forNonisolated(false /*unsafe*/);
+      }
+    }
+
+    return getActorIsolationOfContext(origConstant->getInnermostDeclContext());
+  }
+
+  if (constant) {
+    // TODO: It should to be possible to `getActorIsolation` if
+    // reference is to a decl instead of trying to get isolation
+    // from the reference kind, the attributes, or the context.
+
+    if (constant->kind == SILDeclRef::Kind::Deallocator) {
+      return ActorIsolation::forNonisolated(false);
+    }
+
+    if (auto *decl = constant->getAbstractFunctionDecl()) {
+      if (auto *nonisolatedAttr =
+              decl->getAttrs().getAttribute<NonisolatedAttr>()) {
+        if (nonisolatedAttr->isNonSending())
+          return ActorIsolation::forCallerIsolationInheriting();
+      }
+
+      if (decl->getAttrs().hasAttribute<ConcurrentAttr>()) {
+        return ActorIsolation::forNonisolated(false /*unsafe*/);
+      }
+    }
+
+    if (auto *closure = constant->getAbstractClosureExpr()) {
+      if (auto isolation = closure->getActorIsolation())
+        return isolation;
+    }
+
+    return getActorIsolationOfContext(constant->getInnermostDeclContext());
+  }
+
+  if (substFnInterfaceType->hasExtInfo() &&
+      substFnInterfaceType->getExtInfo().getIsolation().isNonIsolatedCaller()) {
+    // If our function type is a nonisolated caller and we can not infer from
+    // our constant, we must be caller isolation inheriting.
+    return ActorIsolation::forCallerIsolationInheriting();
+  }
+
+  return {};
+}
+
 /// Create the appropriate SIL function type for the given formal type
 /// and conventions.
 ///
@@ -2617,39 +2679,8 @@ static CanSILFunctionType getSILFunctionType(
   SmallBitVector addressableParams;
   SmallBitVector conditionallyAddressableParams;
   {
-    std::optional<ActorIsolation> actorIsolation;
-    if (constant) {
-      // TODO: It should to be possible to `getActorIsolation` if
-      // reference is to a decl instead of trying to get isolation
-      // from the reference kind, the attributes, or the context.
-
-      if (constant->kind == SILDeclRef::Kind::Deallocator) {
-        actorIsolation = ActorIsolation::forNonisolated(false);
-      } else if (auto *decl = constant->getAbstractFunctionDecl()) {
-        if (auto *nonisolatedAttr =
-                decl->getAttrs().getAttribute<NonisolatedAttr>()) {
-          if (nonisolatedAttr->isNonSending())
-            actorIsolation = ActorIsolation::forCallerIsolationInheriting();
-        } else if (decl->getAttrs().hasAttribute<ConcurrentAttr>()) {
-          actorIsolation = ActorIsolation::forNonisolated(false /*unsafe*/);
-        }
-      } else if (auto *closure = constant->getAbstractClosureExpr()) {
-        if (auto isolation = closure->getActorIsolation())
-          actorIsolation = isolation;
-      }
-
-      if (!actorIsolation) {
-        actorIsolation =
-            getActorIsolationOfContext(constant->getInnermostDeclContext());
-      }
-    } else if (substFnInterfaceType->hasExtInfo() &&
-               substFnInterfaceType->getExtInfo()
-                   .getIsolation()
-                   .isNonIsolatedCaller()) {
-      // If our function type is a nonisolated caller and we can not infer from
-      // our constant, we must be caller isolation inheriting.
-      actorIsolation = ActorIsolation::forCallerIsolationInheriting();
-    }
+    auto actorIsolation = getSILFunctionTypeActorIsolation(
+        substFnInterfaceType, origConstant, constant);
     DestructureInputs destructurer(expansionContext, TC, conventions,
                                    foreignInfo, actorIsolation, inputs,
                                    parameterMap,
diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp
index 01b92f44244cc..863be2ee5a898 100644
--- a/lib/SIL/Verifier/SILVerifier.cpp
+++ b/lib/SIL/Verifier/SILVerifier.cpp
@@ -7659,57 +7659,111 @@ void SILVTable::verify(const SILModule &M) const {
 }
 
 /// Verify that a witness table follows invariants.
-void SILWitnessTable::verify(const SILModule &M) const {
-  if (!verificationEnabled(M))
+void SILWitnessTable::verify(const SILModule &mod) const {
+  if (!verificationEnabled(mod))
     return;
 
   if (isDeclaration())
     assert(getEntries().empty() &&
            "A witness table declaration should not have any entries.");
 
-  for (const Entry &E : getEntries())
-    if (E.getKind() == SILWitnessTable::WitnessKind::Method) {
-      SILFunction *F = E.getMethodWitness().Witness;
-      if (F) {
-        // If a SILWitnessTable is going to be serialized, it must only
-        // reference public or serializable functions.
-        if (isAnySerialized()) {
-          assert(F->hasValidLinkageForFragileRef(getSerializedKind()) &&
-                 "Fragile witness tables should not reference "
-                 "less visible functions.");
-        }
+  for (const Entry &entry : getEntries()) {
+    if (entry.getKind() != SILWitnessTable::WitnessKind::Method)
+      continue;
+
+    auto *witnessFunction = entry.getMethodWitness().Witness;
+    if (!witnessFunction)
+      continue;
+
+    // If a SILWitnessTable is going to be serialized, it must only
+    // reference public or serializable functions.
+    if (isAnySerialized()) {
+      assert(
+          witnessFunction->hasValidLinkageForFragileRef(getSerializedKind()) &&
+          "Fragile witness tables should not reference "
+          "less visible functions.");
+    }
 
-        assert(F->getLoweredFunctionType()->getRepresentation() ==
+    assert(witnessFunction->getLoweredFunctionType()->getRepresentation() ==
                SILFunctionTypeRepresentation::WitnessMethod &&
-               "Witnesses must have witness_method representation.");
+           "Witnesses must have witness_method representation.");
+
+    if (mod.getStage() != SILStage::Lowered &&
+        !mod.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
+      // Note the direction of the compatibility check: the witness
+      // function must be compatible with being used as the requirement
+      // type.
+      auto baseInfo = witnessFunction->getModule().Types.getConstantInfo(
+          TypeExpansionContext::minimal(),
+          entry.getMethodWitness().Requirement);
+      SmallString<32> baseName;
+      {
+        llvm::raw_svector_ostream os(baseName);
+        entry.getMethodWitness().Requirement.print(os);
       }
+
+      SILVerifier(*witnessFunction, /*calleeCache=*/nullptr,
+                  /*SingleFunction=*/true,
+                  /*checkLinearLifetime=*/false)
+          .requireABICompatibleFunctionTypes(
+              witnessFunction->getLoweredFunctionType(),
+              baseInfo.getSILType().castTo<SILFunctionType>(),
+              "witness table entry for " + baseName + " must be ABI-compatible",
+              *witnessFunction);
     }
+  }
 }
 
 /// Verify that a default witness table follows invariants.
-void SILDefaultWitnessTable::verify(const SILModule &M) const {
-#ifndef NDEBUG
-  for (const Entry &E : getEntries()) {
+void SILDefaultWitnessTable::verify(const SILModule &mod) const {
+  if (!verificationEnabled(mod))
+    return;
+
+  for (const Entry &entry : getEntries()) {
     // FIXME: associated type witnesses.
-    if (!E.isValid() || E.getKind() != SILWitnessTable::Method)
+    if (!entry.isValid() || entry.getKind() != SILWitnessTable::Method)
       continue;
 
-    SILFunction *F = E.getMethodWitness().Witness;
-    if (!F)
+    auto *witnessFunction = entry.getMethodWitness().Witness;
+    if (!witnessFunction)
       continue;
 
 #if 0
     // FIXME: For now, all default witnesses are private.
-    assert(F->hasValidLinkageForFragileRef(IsSerialized) &&
+    assert(witnessFunction->hasValidLinkageForFragileRef(IsSerialized) &&
            "Default witness tables should not reference "
            "less visible functions.");
 #endif
 
-    assert(F->getLoweredFunctionType()->getRepresentation() ==
-           SILFunctionTypeRepresentation::WitnessMethod &&
+    assert(witnessFunction->getLoweredFunctionType()->getRepresentation() ==
+               SILFunctionTypeRepresentation::WitnessMethod &&
            "Default witnesses must have witness_method representation.");
+
+    if (mod.getStage() != SILStage::Lowered &&
+        !mod.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
+      // Note the direction of the compatibility check: the witness
+      // function must be compatible with being used as the requirement
+      // type.
+      auto baseInfo = witnessFunction->getModule().Types.getConstantInfo(
+          TypeExpansionContext::minimal(),
+          entry.getMethodWitness().Requirement);
+      SmallString<32> baseName;
+      {
+        llvm::raw_svector_ostream os(baseName);
+        entry.getMethodWitness().Requirement.print(os);
+      }
+
+      SILVerifier(*witnessFunction, /*calleeCache=*/nullptr,
+                  /*SingleFunction=*/true,
+                  /*checkLinearLifetime=*/false)
+          .requireABICompatibleFunctionTypes(
+              witnessFunction->getLoweredFunctionType(),
+              baseInfo.getSILType().castTo<SILFunctionType>(),
+              "default witness table entry for " + baseName +
+                  " must be ABI-compatible",
+              *witnessFunction);
+    }
   }
-#endif
 }
 
 /// Verify that a global variable follows invariants.
diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h
index 41781cff21b83..b37ee574286e1 100644
--- a/lib/SILGen/SILGenFunction.h
+++ b/lib/SILGen/SILGenFunction.h
@@ -2624,8 +2624,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
   void collectThunkParams(
       SILLocation loc, SmallVectorImpl<ManagedValue> &params,
       SmallVectorImpl<ManagedValue> *indirectResultParams = nullptr,
-      SmallVectorImpl<ManagedValue> *indirectErrorParams = nullptr,
-      ThunkGenOptions options = {});
+      SmallVectorImpl<ManagedValue> *indirectErrorParams = nullptr);
 
   /// Build the type of a function transformation thunk.
   CanSILFunctionType buildThunkType(CanSILFunctionType &sourceType,
diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp
index 17d712f5e8e3e..aecc3d7814142 100644
--- a/lib/SILGen/SILGenPoly.cpp
+++ b/lib/SILGen/SILGenPoly.cpp
@@ -986,7 +986,7 @@ ManagedValue Transform::transformTuple(ManagedValue inputTuple,
 void SILGenFunction::collectThunkParams(
     SILLocation loc, SmallVectorImpl<ManagedValue> &params,
     SmallVectorImpl<ManagedValue> *indirectResults,
-    SmallVectorImpl<ManagedValue> *indirectErrors, ThunkGenOptions options) {
+    SmallVectorImpl<ManagedValue> *indirectErrors) {
   // Add the indirect results.
   for (auto resultTy : F.getConventions().getIndirectSILResultTypes(
            getTypeExpansionContext())) {
@@ -5464,7 +5464,7 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
 
   SmallVector<ManagedValue, 8> params;
   SmallVector<ManagedValue, 4> indirectResultParams;
-  SGF.collectThunkParams(loc, params, &indirectResultParams, nullptr, options);
+  SGF.collectThunkParams(loc, params, &indirectResultParams);
 
   // Ignore the self parameter at the SIL level. IRGen will use it to
   // recover type metadata.
@@ -6945,10 +6945,6 @@ SILGenFunction::emitVTableThunk(SILDeclRef base,
   cleanupLoc.markAutoGenerated();
   Scope scope(Cleanups, cleanupLoc);
 
-  SmallVector<ManagedValue, 8> thunkArgs;
-  SmallVector<ManagedValue, 8> thunkIndirectResults;
-  collectThunkParams(loc, thunkArgs, &thunkIndirectResults);
-
   CanSILFunctionType derivedFTy;
   if (baseLessVisibleThanDerived) {
     derivedFTy =
@@ -6973,16 +6969,41 @@ SILGenFunction::emitVTableThunk(SILDeclRef base,
             ->substGenericArgs(subs)->getCanonicalType());
   }
 
-  // Emit the indirect return and arguments.
   auto thunkTy = F.getLoweredFunctionType();
 
-  SmallVector<ManagedValue, 8> substArgs;
+  using ThunkGenFlag = SILGenFunction::ThunkGenFlag;
+  auto options = SILGenFunction::ThunkGenOptions();
 
+  {
+    auto thunkIsolatedParam = thunkTy->maybeGetIsolatedParameter();
+    if (thunkIsolatedParam &&
+        thunkIsolatedParam->hasOption(SILParameterInfo::ImplicitLeading))
+      options |= ThunkGenFlag::ThunkHasImplicitIsolatedParam;
+    auto derivedIsolatedParam = derivedFTy->maybeGetIsolatedParameter();
+    if (derivedIsolatedParam &&
+        derivedIsolatedParam->hasOption(SILParameterInfo::ImplicitLeading))
+      options |= ThunkGenFlag::CalleeHasImplicitIsolatedParam;
+  }
+
+  SmallVector<ManagedValue, 8> thunkArgs;
+  SmallVector<ManagedValue, 8> thunkIndirectResults;
+  collectThunkParams(loc, thunkArgs, &thunkIndirectResults);
+
+  // Emit the indirect return and arguments.
+  SmallVector<ManagedValue, 8> substArgs;
   AbstractionPattern outputOrigType(outputSubstType);
 
+  auto derivedFTyParamInfo = derivedFTy->getParameters();
+
+  // If we are transforming to a callee with an implicit param, drop the
+  // implicit param so that we can insert it again later. This ensures
+  // TranslateArguments does not need to know about this.
+  if (options.contains(ThunkGenFlag::CalleeHasImplicitIsolatedParam))
+    derivedFTyParamInfo = derivedFTyParamInfo.drop_front();
+
   // Reabstract the arguments.
   TranslateArguments(*this, loc, thunkArgs, substArgs,
-                     derivedFTy, derivedFTy->getParameters())
+                     derivedFTy, derivedFTyParamInfo)
     .process(outputOrigType,
              outputSubstType.getParams(),
              inputOrigType,
@@ -7012,8 +7033,61 @@ SILGenFunction::emitVTableThunk(SILDeclRef base,
     }
   }
 
+  // Now that we have translated arguments and inserted our thunk indirect
+  // parameters... before we forward those arguments, insert the implicit
+  // leading parameter.
+  if (options.contains(ThunkGenFlag::CalleeHasImplicitIsolatedParam)) {
+    auto baseIsolation =
+        swift::getActorIsolation(base.getAbstractFunctionDecl());
+    switch (baseIsolation) {
+    case ActorIsolation::Unspecified:
+    case ActorIsolation::Nonisolated:
+    case ActorIsolation::NonisolatedUnsafe:
+      args.push_back(emitNonIsolatedIsolation(loc).getValue());
+      break;
+    case ActorIsolation::Erased:
+      llvm::report_fatal_error("Found erased actor isolation?!");
+      break;
+    case ActorIsolation::GlobalActor: {
+      auto globalActor = baseIsolation.getGlobalActor()->getCanonicalType();
+      args.push_back(emitGlobalActorIsolation(loc, globalActor).getValue());
+      break;
+    }
+    case ActorIsolation::ActorInstance:
+    case ActorIsolation::CallerIsolationInheriting: {
+      auto derivedIsolation =
+          swift::getActorIsolation(derived.getAbstractFunctionDecl());
+      switch (derivedIsolation) {
+      case ActorIsolation::Unspecified:
+      case ActorIsolation::Nonisolated:
+      case ActorIsolation::NonisolatedUnsafe:
+        args.push_back(emitNonIsolatedIsolation(loc).getValue());
+        break;
+      case ActorIsolation::Erased:
+        llvm::report_fatal_error("Found erased actor isolation?!");
+        break;
+      case ActorIsolation::GlobalActor: {
+        auto globalActor =
+            derivedIsolation.getGlobalActor()->getCanonicalType();
+        args.push_back(emitGlobalActorIsolation(loc, globalActor).getValue());
+        break;
+      }
+      case ActorIsolation::ActorInstance:
+      case ActorIsolation::CallerIsolationInheriting: {
+        auto isolatedArg = F.maybeGetIsolatedArgument();
+        assert(isolatedArg);
+        args.push_back(isolatedArg);
+        break;
+      }
+      }
+      break;
+    }
+    }
+  }
+
   // Then, the arguments.
-  forwardFunctionArguments(*this, loc, derivedFTy, substArgs, args);
+  forwardFunctionArguments(*this, loc, derivedFTy, substArgs, args,
+                           options);
 
   // Create the call.
   SILValue derivedRef;
@@ -7316,7 +7390,7 @@ void SILGenFunction::emitProtocolWitness(
 
   SmallVector<ManagedValue, 8> origParams;
   SmallVector<ManagedValue, 8> thunkIndirectResults;
-  collectThunkParams(loc, origParams, &thunkIndirectResults, nullptr, options);
+  collectThunkParams(loc, origParams, &thunkIndirectResults);
 
   if (witness.getDecl()->requiresUnavailableDeclABICompatibilityStubs())
     emitApplyOfUnavailableCodeReached();
diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp
index f910c93f6dade..26b2b2a967d7f 100644
--- a/lib/SILGen/SILGenType.cpp
+++ b/lib/SILGen/SILGenType.cpp
@@ -903,6 +903,12 @@ SILFunction *SILGenModule::emitProtocolWitness(
                           witness.getEnterIsolation());
 
   emitLazyConformancesForFunction(f);
+
+  if (auto isolation = getSILFunctionTypeActorIsolation(
+          reqtSubstTy, requirement, witnessRef)) {
+    f->setActorIsolation(*isolation);
+  }
+
   return f;
 }
 
diff --git a/test/Concurrency/attr_execution/classes_silgen.swift b/test/Concurrency/attr_execution/classes_silgen.swift
new file mode 100644
index 0000000000000..df3442594bb32
--- /dev/null
+++ b/test/Concurrency/attr_execution/classes_silgen.swift
@@ -0,0 +1,78 @@
+// RUN: %target-swift-emit-silgen %s -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple -DSWIFT_FIVE | %FileCheck --implicit-check-not=thunk %s
+// RUN: %target-swift-emit-silgen %s -swift-version 6 -module-name attr_execution_silgen -target %target-swift-5.1-abi-triple | %FileCheck --implicit-check-not=thunk %s
+
+// REQUIRES: asserts
+// REQUIRES: concurrency
+
+// We should only produce thunks when going to/from nonisolated(nonsending)
+// since that is the only thing that makes a true ABI change since we have an
+// extra parameter. For isolation purposes, we can rely on async functions
+// hopping in their prolog and not need a thunk for the purposes of isolation changing.
+
+// NOTE: We use implicit-check-not to make sure we do not create any other
+// thunks beyond the ones we pattern match.
+
+class SuperKlass {
+  nonisolated(nonsending) func callerTest() async {}
+  @concurrent func concurrentTest() async {}
+  @MainActor func mainActorTest() async {}
+}
+
+class AllDefault : SuperKlass {
+  // This doesn't need a thunk since we infer it is nonisolated(nonsending).
+  override func callerTest() async {}
+  override func concurrentTest() async {}
+  override func mainActorTest() async {}
+}
+
+class AllConcurrent : SuperKlass {
+  // CHECK-LABEL: vtable thunk for SuperKlass.callerTest() dispatching to AllConcurrent.callerTest()
+  // CHECK-NEXT: sil private [thunk] [ossa] @$s21attr_execution_silgen13AllConcurrentC10callerTestyyYaFAA10SuperKlassCADyyYaFTV : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @guaranteed AllConcurrent) -> () {
+  // CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional<any Actor>, [[PARAM:%.*]] : @guaranteed $AllConcurrent):
+  // CHECK:  [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen13AllConcurrentC10callerTestyyYaF : $@convention(method) @async (@guaranteed AllConcurrent) -> ()
+  // CHECK:  apply [[FUNC]]([[PARAM]])
+  // CHECK: } // end sil function '$s21attr_execution_silgen13AllConcurrentC10callerTestyyYaFAA10SuperKlassCADyyYaFTV'
+  @concurrent override func callerTest() async {}
+  @concurrent override func concurrentTest() async {}
+  @concurrent override func mainActorTest() async {}
+}
+
+class AllNonIsolatedUnsafe : SuperKlass {
+  override nonisolated(nonsending) func callerTest() async {}
+
+  // CHECK-LABEL: // vtable thunk for SuperKlass.concurrentTest() dispatching to AllNonIsolatedUnsafe.concurrentTest()
+  // CHECK-NEXT: sil private [thunk] [ossa] @$s21attr_execution_silgen20AllNonIsolatedUnsafeC14concurrentTestyyYaFAA10SuperKlassCADyyYaFTV : $@convention(method) @async (@guaranteed AllNonIsolatedUnsafe) -> () {
+  // CHECK: bb0([[ARG:%.*]] : @guaranteed
+  // CHECK:   [[ACTOR:%.*]] = enum $Optional<any Actor>, #Optional.none!enumelt
+  // CHECK:   [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen20AllNonIsolatedUnsafeC14concurrentTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @guaranteed AllNonIsolatedUnsafe) -> ()
+  // CHECK:   apply [[FUNC]]([[ACTOR]], [[ARG]])
+  // CHECK: } // end sil function '$s21attr_execution_silgen20AllNonIsolatedUnsafeC14concurrentTestyyYaFAA10SuperKlassCADyyYaFTV'
+  override nonisolated(nonsending) func concurrentTest() async {}
+
+  // CHECK-LABEL: // vtable thunk for SuperKlass.mainActorTest() dispatching to AllNonIsolatedUnsafe.mainActorTest()
+  // CHECK-NEXT: sil private [thunk] [ossa] @$s21attr_execution_silgen20AllNonIsolatedUnsafeC13mainActorTestyyYaFAA10SuperKlassCADyyYaFTV : $@convention(method) @async (@guaranteed AllNonIsolatedUnsafe) -> () {
+  // CHECK: bb0([[ARG:%.*]] : @guaranteed $AllNonIsolatedUnsafe):
+  // CHECK:   [[ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor
+  // CHECK:   [[ACTOR_E:%.*]] = init_existential_ref [[ACTOR]]
+  // CHECK:   [[ACTOR_E_OPT:%.*]] = enum $Optional<any Actor>, #Optional.some!enumelt, [[ACTOR_E]]
+  // CHECK:   [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen20AllNonIsolatedUnsafeC13mainActorTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @guaranteed AllNonIsolatedUnsafe) -> ()
+  // CHECK:   apply [[FUNC]]([[ACTOR_E_OPT]], [[ARG]])
+  // CHECK: } // end sil function '$s21attr_execution_silgen20AllNonIsolatedUnsafeC13mainActorTestyyYaFAA10SuperKlassCADyyYaFTV'
+  override nonisolated(nonsending) func mainActorTest() async {}
+}
+
+// CHECK-LABEL: sil_vtable AllConcurrent {
+// CHECK-NEXT:   #SuperKlass.callerTest: (SuperKlass) -> () async -> () : @$s21attr_execution_silgen13AllConcurrentC10callerTestyyYaFAA10SuperKlassCADyyYaFTV [override]	// vtable thunk for SuperKlass.callerTest() dispatching to AllConcurrent.callerTest()
+// CHECK-NEXT:   #SuperKlass.concurrentTest: (SuperKlass) -> () async -> () : @$s21attr_execution_silgen13AllConcurrentC14concurrentTestyyYaF [override]	// AllConcurrent.concurrentTest()
+// CHECK-NEXT:   #SuperKlass.mainActorTest: (SuperKlass) -> () async -> () : @$s21attr_execution_silgen13AllConcurrentC13mainActorTestyyYaF [override]	// AllConcurrent.mainActorTest()
+// CHECK-NEXT:   #SuperKlass.init!allocator: (SuperKlass.Type) -> () -> SuperKlass : @$s21attr_execution_silgen13AllConcurrentCACycfC [override]	// AllConcurrent.__allocating_init()
+// CHECK-NEXT:   #AllConcurrent.deinit!deallocator: @$s21attr_execution_silgen13AllConcurrentCfD	// AllConcurrent.__deallocating_deinit
+// CHECK-NEXT: }
+
+// CHECK-LABEL: sil_vtable AllNonIsolatedUnsafe {
+// CHECK-NEXT:   #SuperKlass.callerTest: (SuperKlass) -> () async -> () : @$s21attr_execution_silgen20AllNonIsolatedUnsafeC10callerTestyyYaF [override]	// AllNonIsolatedUnsafe.callerTest()
+// CHECK-NEXT:   #SuperKlass.concurrentTest: (SuperKlass) -> () async -> () : @$s21attr_execution_silgen20AllNonIsolatedUnsafeC14concurrentTestyyYaFAA10SuperKlassCADyyYaFTV [override]	// vtable thunk for SuperKlass.concurrentTest() dispatching to AllNonIsolatedUnsafe.concurrentTest()
+// CHECK-NEXT:   #SuperKlass.mainActorTest: (SuperKlass) -> () async -> () : @$s21attr_execution_silgen20AllNonIsolatedUnsafeC13mainActorTestyyYaFAA10SuperKlassCADyyYaFTV [override]	// vtable thunk for SuperKlass.mainActorTest() dispatching to AllNonIsolatedUnsafe.mainActorTest()
+// CHECK-NEXT:   #SuperKlass.init!allocator: (SuperKlass.Type) -> () -> SuperKlass : @$s21attr_execution_silgen20AllNonIsolatedUnsafeCACycfC [override]	// AllNonIsolatedUnsafe.__allocating_init()
+// CHECK-NEXT:   #AllNonIsolatedUnsafe.deinit!deallocator: @$s21attr_execution_silgen20AllNonIsolatedUnsafeCfD	// AllNonIsolatedUnsafe.__deallocating_deinit
+// CHECK-NEXT: }
diff --git a/test/Concurrency/attr_execution/protocols_silgen.swift b/test/Concurrency/attr_execution/protocols_silgen.swift
index 37bdaa54649bb..10b513d11e4fe 100644
--- a/test/Concurrency/attr_execution/protocols_silgen.swift
+++ b/test/Concurrency/attr_execution/protocols_silgen.swift
@@ -48,8 +48,8 @@ struct AllCaller : P {
   // CHECK: } // end sil function '$s21attr_execution_silgen9AllCallerVAA1PA2aDP10callerTestyyYaFTW'
   nonisolated(nonsending) func callerTest() async {}
 
-  // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen9AllCallerVAA1PA2aDP14concurrentTestyyYaFTW : $@convention(witness_method: P) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @in_guaranteed AllCaller) -> () {
-  // CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional<any Actor>, [[SELF:%.*]] : $*AllCaller):
+  // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen9AllCallerVAA1PA2aDP14concurrentTestyyYaFTW : $@convention(witness_method: P) @async (@in_guaranteed AllCaller) -> () {
+  // CHECK: bb0([[SELF:%.*]] : $*AllCaller):
   // CHECK:   [[LOAD:%.*]] = load [trivial] [[SELF]]
   // CHECK:   [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen9AllCallerV14concurrentTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, AllCaller) -> ()
   // CHECK:   [[NIL:%.*]] = enum $Optional<any Actor>, #Optional.none!enumelt
@@ -57,8 +57,8 @@ struct AllCaller : P {
   // CHECK: } // end sil function '$s21attr_execution_silgen9AllCallerVAA1PA2aDP14concurrentTestyyYaFTW'
   nonisolated(nonsending) func concurrentTest() async {}
 
-  // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen9AllCallerVAA1PA2aDP13mainActorTestyyYaFTW : $@convention(witness_method: P) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @in_guaranteed AllCaller) -> () {
-  // CHECK: bb0({{%.*}} : @guaranteed $Optional<any Actor>, [[SELF:%.*]] : $*AllCaller):
+  // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen9AllCallerVAA1PA2aDP13mainActorTestyyYaFTW : $@convention(witness_method: P) @async (@in_guaranteed AllCaller) -> () {
+  // CHECK: bb0([[SELF:%.*]] : $*AllCaller):
   // CHECK:   [[LOAD:%.*]] = load [trivial] [[SELF]]
   // CHECK:   [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen9AllCallerV13mainActorTestyyYaF : $@convention(method) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, AllCaller) -> ()
   // CHECK:   [[MAIN_ACTOR:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick MainActor.Type) -> @owned MainActor
@@ -70,11 +70,8 @@ struct AllCaller : P {
 }
 
 struct AllConcurrent : P {
-  // TODO: This seems wrong. We need to have our thunk have the implicit
-  // isolated parameter from an ABI perspective.
-  //
-  // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen13AllConcurrentVAA1PA2aDP10callerTestyyYaFTW : $@convention(witness_method: P) @async (@in_guaranteed AllConcurrent) -> () {
-  // CHECK: bb0([[SELF:%.*]] :
+  // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen13AllConcurrentVAA1PA2aDP10callerTestyyYaFTW : $@convention(witness_method: P) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @in_guaranteed AllConcurrent) -> () {
+  // CHECK: bb0([[ACTOR]] : @guaranteed $Optional<any Actor>, [[SELF:%.*]] :
   // CHECK:   [[LOAD:%.*]] = load [trivial] [[SELF]]
   // CHECK:   [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen13AllConcurrentV10callerTestyyYaF : $@convention(method) @async (AllConcurrent) -> ()
   // CHECK:   apply [[FUNC]]([[LOAD]])
@@ -99,11 +96,8 @@ struct AllConcurrent : P {
 }
 
 struct AllMainActor : P {
-  // TODO: This is incorrect from an ABI perspective. The witness needs to have
-  // the implicit isolated parameter.
-  //
-  // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen12AllMainActorVAA1PA2aDP10callerTestyyYaFTW : $@convention(witness_method: P) @async (@in_guaranteed AllMainActor) -> () {
-  // CHECK: bb0([[SELF:%.*]] :
+  // CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s21attr_execution_silgen12AllMainActorVAA1PA2aDP10callerTestyyYaFTW : $@convention(witness_method: P) @async (@sil_isolated @sil_implicit_leading_param @guaranteed Optional<any Actor>, @in_guaranteed AllMainActor) -> () {
+  // CHECK: bb0([[ACTOR:%.*]] : @guaranteed $Optional<any Actor>, [[SELF:%.*]] :
   // CHECK:   [[LOAD:%.*]] = load [trivial] [[SELF]]
   // CHECK:   [[FUNC:%.*]] = function_ref @$s21attr_execution_silgen12AllMainActorV10callerTestyyYaF : $@convention(method) @async (AllMainActor) -> ()
   // CHECK:   apply [[FUNC]]([[LOAD]])
diff --git a/test/SIL/Parser/witness_protocol_from_import.sil b/test/SIL/Parser/witness_protocol_from_import.sil
index 7e417e71e2b0c..4f3556730dcdd 100644
--- a/test/SIL/Parser/witness_protocol_from_import.sil
+++ b/test/SIL/Parser/witness_protocol_from_import.sil
@@ -10,7 +10,7 @@ enum X {
   case twotransp
 }
 
-sil @_TTWO5test21Xs9EquatableS_FS1_oi2eeUS1___fMQPS1_FT3lhsS2_3rhsS2__Sb : $@convention(witness_method: Equatable) (@in X, @in X, @thick X.Type) -> Bool {
+sil @_TTWO5test21Xs9EquatableS_FS1_oi2eeUS1___fMQPS1_FT3lhsS2_3rhsS2__Sb : $@convention(witness_method: Equatable) (@in_guaranteed X, @in_guaranteed X, @thick X.Type) -> Bool {
 bb0(%0 : $*X, %1 : $*X, %2 : $@thick X.Type):
   %3 = load %0 : $*X
   %4 = load %1 : $*X
@@ -21,10 +21,9 @@ bb0(%0 : $*X, %1 : $*X, %2 : $@thick X.Type):
 }
 
 
-sil @_TTWO5test21Xs8HashableS_FS1_g9hashValueSi : $@convention(witness_method: Hashable) (@inout X) -> Int {
+sil @_TTWO5test21Xs8HashableS_FS1_g9hashValueSi : $@convention(witness_method: Hashable) (@in_guaranteed X) -> Int {
 bb0(%0 : $*X):
   %1 = load %0 : $*X
-
   %2 = function_ref @_TFO5test21Xg9hashValueSi : $@convention(method) (X) -> Int
   %3 = apply %2(%1) : $@convention(method) (X) -> Int
   return %3 : $Int
diff --git a/test/SIL/Parser/witness_tables.sil b/test/SIL/Parser/witness_tables.sil
index 193022d22dfb2..446172bd07166 100644
--- a/test/SIL/Parser/witness_tables.sil
+++ b/test/SIL/Parser/witness_tables.sil
@@ -15,7 +15,7 @@ bb0(%0 : $ConformingAssoc):
   return %2 : $()
 }
 
-sil @_TTWV14witness_tables15ConformingAssocS_9AssocReqtS_FS1_14requiredMethodU_fRQPS1_FT_T_ : $@convention(witness_method: AssocReqt) (@inout ConformingAssoc) -> () {
+sil @_TTWV14witness_tables15ConformingAssocS_9AssocReqtS_FS1_14requiredMethodU_fRQPS1_FT_T_ : $@convention(witness_method: AssocReqt) (@in_guaranteed ConformingAssoc) -> () {
 bb0(%0 : $*ConformingAssoc):
   %1 = load %0 : $*ConformingAssoc
   %2 = function_ref @_TFV14witness_tables15ConformingAssoc14requiredMethodfS0_FT_T_ : $@convention(method) (ConformingAssoc) -> ()
diff --git a/test/SIL/Serialization/witness_tables.sil b/test/SIL/Serialization/witness_tables.sil
index dd5b4e5ebd2e8..369a2beece710 100644
--- a/test/SIL/Serialization/witness_tables.sil
+++ b/test/SIL/Serialization/witness_tables.sil
@@ -15,7 +15,7 @@ bb0(%0 : $ConformingAssoc):
   return %2 : $()
 }
 
-sil [serialized] @_TTWV14witness_tables15ConformingAssocS_9AssocReqtS_FS1_14requiredMethodU_fRQPS1_FT_T_ : $@convention(witness_method: AssocReqt) (@inout ConformingAssoc) -> () {
+sil [serialized] @_TTWV14witness_tables15ConformingAssocS_9AssocReqtS_FS1_14requiredMethodU_fRQPS1_FT_T_ : $@convention(witness_method: AssocReqt) (@in_guaranteed ConformingAssoc) -> () {
 bb0(%0 : $*ConformingAssoc):
   %1 = load %0 : $*ConformingAssoc
   %2 = function_ref @_TFV14witness_tables15ConformingAssoc14requiredMethodfS0_FT_T_ : $@convention(method) (ConformingAssoc) -> ()
diff --git a/test/SILOptimizer/closure_specialize.sil b/test/SILOptimizer/closure_specialize.sil
index b6fa0b8c0ed67..0b03a690953ee 100644
--- a/test/SILOptimizer/closure_specialize.sil
+++ b/test/SILOptimizer/closure_specialize.sil
@@ -531,9 +531,9 @@ public struct S : P {
 }
 
 // CHECK-LABEL: sil shared @$s4test1SVAA1PA2aDP3fooyS2iycFZTW8closure2SiTf1cn_n : $@convention(thin) (@thick S.Type, Int) -> Int
-sil @$s4test1SVAA1PA2aDP3fooyS2iycFZTW : $@convention(witness_method: P) (@owned @callee_owned () -> Int, @thick S.Type) -> Int {
-bb0(%0 : $@callee_owned () -> Int, %1 : $@thick S.Type):
-  %3 = apply %0() : $@callee_owned () -> Int
+sil @$s4test1SVAA1PA2aDP3fooyS2iycFZTW : $@convention(witness_method: P) (@guaranteed @noescape @callee_guaranteed () -> Int, @thick S.Type) -> Int {
+bb0(%0 : $@noescape @callee_guaranteed () -> Int, %1 : $@thick S.Type):
+  %3 = apply %0() : $@noescape @callee_guaranteed () -> Int
   return %3 : $Int
 }
 
@@ -545,10 +545,11 @@ bb0(%0 : $Int):
 sil @call_witness_method : $@convention(thin) (Int, S) -> Int {
 bb0(%0 : $Int, %1 : $S):
   %3 = function_ref @closure2 : $@convention(thin) (Int) -> Int
-  %4 = partial_apply %3(%0) : $@convention(thin) (Int) -> Int
+  %4 = partial_apply [callee_guaranteed] %3(%0) : $@convention(thin) (Int) -> Int
+  %4a = convert_escape_to_noescape %4 : $@callee_guaranteed () -> Int to $@noescape @callee_guaranteed () -> Int
   %5 = metatype $@thick S.Type
-  %6 = function_ref @$s4test1SVAA1PA2aDP3fooyS2iycFZTW : $@convention(witness_method: P) (@owned @callee_owned () -> Int, @thick S.Type) -> Int
-  %7 = apply %6(%4, %5) : $@convention(witness_method: P) (@owned @callee_owned () -> Int, @thick S.Type) -> Int
+  %6 = function_ref @$s4test1SVAA1PA2aDP3fooyS2iycFZTW : $@convention(witness_method: P) (@guaranteed @noescape @callee_guaranteed () -> Int, @thick S.Type) -> Int
+  %7 = apply %6(%4a, %5) : $@convention(witness_method: P) (@guaranteed @noescape @callee_guaranteed () -> Int, @thick S.Type) -> Int
   return %7 : $Int
 }
 
diff --git a/test/SILOptimizer/devirt_default_witness_method.sil b/test/SILOptimizer/devirt_default_witness_method.sil
index 76aa41f5fad86..10bb2603cdf1e 100644
--- a/test/SILOptimizer/devirt_default_witness_method.sil
+++ b/test/SILOptimizer/devirt_default_witness_method.sil
@@ -43,7 +43,7 @@ public protocol ResilientProtocolWithGeneric {
   func defaultB<T: Q>(_: T)
 }
 
-sil @defaultB : $@convention(witness_method: ResilientProtocolWithGeneric) <Self where Self : ResilientProtocolWithGeneric><T where T : Q> (@in T, @in_guaranteed Self) -> () {
+sil @defaultB : $@convention(witness_method: ResilientProtocolWithGeneric) <Self where Self : ResilientProtocolWithGeneric><T where T : Q> (@in_guaranteed T, @in_guaranteed Self) -> () {
 bb0(%0 : $*T, %1 : $*Self):
   %result = tuple ()
   return %result : $()
@@ -89,8 +89,8 @@ bb0(%0 : $*ConformingGenericStruct<Int>):
 
 // CHECK-LABEL: test_devirt_of_inner_generic_default_witness_method
 // CHECK:       bb0(%0 : $*ConformingGenericStruct<Int>, %1 : $*Int):
-// CHECK:       [[FN:%[0-9]+]] = function_ref @defaultB : $@convention(witness_method: ResilientProtocolWithGeneric) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : Q> (@in τ_1_0, @in_guaranteed τ_0_0) -> ()
-// CHECK:       [[RESULT:%[0-9]+]] = apply [[FN]]<ConformingGenericStruct<Int>, Int>(%1, %0) : $@convention(witness_method: ResilientProtocolWithGeneric) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : Q> (@in τ_1_0, @in_guaranteed τ_0_0) -> ()
+// CHECK:       [[FN:%[0-9]+]] = function_ref @defaultB : $@convention(witness_method: ResilientProtocolWithGeneric) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> ()
+// CHECK:       [[RESULT:%[0-9]+]] = apply [[FN]]<ConformingGenericStruct<Int>, Int>(%1, %0) : $@convention(witness_method: ResilientProtocolWithGeneric) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> ()
 // CHECK:       return [[RESULT]] : $()
 // CHECK:     }
 sil hidden @test_devirt_of_inner_generic_default_witness_method : $@convention(thin) (@in_guaranteed ConformingGenericStruct<Int>, @in Int) -> () {
diff --git a/test/SILOptimizer/devirt_default_witness_method_ownership.sil b/test/SILOptimizer/devirt_default_witness_method_ownership.sil
index ff4c5ff090485..0e382cd876f73 100644
--- a/test/SILOptimizer/devirt_default_witness_method_ownership.sil
+++ b/test/SILOptimizer/devirt_default_witness_method_ownership.sil
@@ -43,7 +43,7 @@ public protocol ResilientProtocolWithGeneric {
   func defaultB<T: Q>(_: T)
 }
 
-sil @defaultB : $@convention(witness_method: ResilientProtocolWithGeneric) <Self where Self : ResilientProtocolWithGeneric><T where T : Q> (@in T, @in_guaranteed Self) -> () {
+sil @defaultB : $@convention(witness_method: ResilientProtocolWithGeneric) <Self where Self : ResilientProtocolWithGeneric><T where T : Q> (@in_guaranteed T, @in_guaranteed Self) -> () {
 bb0(%0 : $*T, %1 : $*Self):
   %result = tuple ()
   return %result : $()
@@ -89,8 +89,8 @@ bb0(%0 : $*ConformingGenericStruct<Int>):
 
 // CHECK-LABEL: test_devirt_of_inner_generic_default_witness_method
 // CHECK:       bb0(%0 : $*ConformingGenericStruct<Int>, %1 : $*Int):
-// CHECK:       [[FN:%[0-9]+]] = function_ref @defaultB : $@convention(witness_method: ResilientProtocolWithGeneric) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : Q> (@in τ_1_0, @in_guaranteed τ_0_0) -> ()
-// CHECK:       [[RESULT:%[0-9]+]] = apply [[FN]]<ConformingGenericStruct<Int>, Int>(%1, %0) : $@convention(witness_method: ResilientProtocolWithGeneric) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : Q> (@in τ_1_0, @in_guaranteed τ_0_0) -> ()
+// CHECK:       [[FN:%[0-9]+]] = function_ref @defaultB : $@convention(witness_method: ResilientProtocolWithGeneric) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> ()
+// CHECK:       [[RESULT:%[0-9]+]] = apply [[FN]]<ConformingGenericStruct<Int>, Int>(%1, %0) : $@convention(witness_method: ResilientProtocolWithGeneric) <τ_0_0 where τ_0_0 : ResilientProtocolWithGeneric><τ_1_0 where τ_1_0 : Q> (@in_guaranteed τ_1_0, @in_guaranteed τ_0_0) -> ()
 // CHECK:       return [[RESULT]] : $()
 // CHECK:     }
 sil hidden [ossa] @test_devirt_of_inner_generic_default_witness_method : $@convention(thin) (@in_guaranteed ConformingGenericStruct<Int>, @in Int) -> () {
diff --git a/test/SILOptimizer/devirt_static_witness_method.sil b/test/SILOptimizer/devirt_static_witness_method.sil
index a016e54f697f4..23c32a2856fd2 100644
--- a/test/SILOptimizer/devirt_static_witness_method.sil
+++ b/test/SILOptimizer/devirt_static_witness_method.sil
@@ -1,4 +1,5 @@
 // RUN: %target-sil-opt -enable-sil-verify-all %s -devirtualizer -sil-combine | %FileCheck %s
+
 sil_stage canonical
 
 import Builtin
@@ -18,13 +19,13 @@ struct S<T> : CanAdd {
 func +<T>(lhs: S<T>, rhs: S<T>) -> S<T>
 
 
-sil hidden [transparent] [thunk] @operator_plus_static_non_generic_witness_for_S : $@convention(witness_method: CanAdd) <T> (@in S<T>, @in S<T>, @thick S<T>.Type) -> @out S<T> {
+sil hidden [transparent] [thunk] @operator_plus_static_non_generic_witness_for_S : $@convention(witness_method: CanAdd) <T> (@in_guaranteed S<T>, @in_guaranteed S<T>, @thick S<T>.Type) -> @out S<T> {
 bb0(%0 : $*S<T>, %1 : $*S<T>, %2 : $*S<T>, %3 : $@thick S<T>.Type) :
   %17 = tuple ()
   return %17 : $()
 }
 
-sil hidden [transparent] [thunk] @operator_plus_static_non_generic_witness : $@convention(witness_method: CanAdd) (@in Int64, @in Int64, @thick Int64.Type) -> @out Int64 {
+sil hidden [transparent] [thunk] @operator_plus_static_non_generic_witness : $@convention(witness_method: CanAdd) (@in_guaranteed Int64, @in_guaranteed Int64, @thick Int64.Type) -> @out Int64 {
 bb0(%0 : $*Int64, %1 : $*Int64, %2 : $*Int64, %3 : $@thick Int64.Type):
   %4 = struct_element_addr %1 : $*Int64, #Int64._value
   %5 = load %4 : $*Builtin.Int64
@@ -44,12 +45,12 @@ bb0(%0 : $*Int64, %1 : $*Int64, %2 : $*Int64, %3 : $@thick Int64.Type):
 // Test that this partial application of a function reference referring to static non generic witness does not crash the IRGen.
 // Such code may be produced e.g. when users refer to global operators defined on builtin types.
 // CHECK-LABEL: sil hidden @test_partial_apply_of_static_witness
-sil hidden @test_partial_apply_of_static_witness : $@convention(thin) () -> @callee_owned (@in Int64, @in Int64) -> @out Int64 {
+sil hidden @test_partial_apply_of_static_witness : $@convention(thin) () -> @callee_owned (@in_guaranteed Int64, @in_guaranteed Int64) -> @out Int64 {
 bb0:
   %1 = metatype $@thick Int64.Type
-  %2 = function_ref @operator_plus_static_non_generic_witness : $@convention(witness_method: CanAdd) (@in Int64, @in Int64, @thick Int64.Type) -> @out Int64
-  %3 = partial_apply %2(%1) : $@convention(witness_method: CanAdd) (@in Int64, @in Int64, @thick Int64.Type) -> @out Int64
-  return %3 : $@callee_owned (@in Int64, @in Int64) -> @out Int64
+  %2 = function_ref @operator_plus_static_non_generic_witness : $@convention(witness_method: CanAdd) (@in_guaranteed Int64, @in_guaranteed Int64, @thick Int64.Type) -> @out Int64
+  %3 = partial_apply %2(%1) : $@convention(witness_method: CanAdd) (@in_guaranteed Int64, @in_guaranteed Int64, @thick Int64.Type) -> @out Int64
+  return %3 : $@callee_owned (@in_guaranteed Int64, @in_guaranteed Int64) -> @out Int64
 }
 
 // Test that this partial application of witness_method can be devirtualized.
@@ -58,12 +59,12 @@ bb0:
 // CHECK: function_ref @operator_plus_static_non_generic_witness_for_S
 // CHECK: partial_apply
 // CHECK: return
-sil hidden @test_devirt_of_partial_apply_of_witness_method : $@convention(thin) () -> @callee_owned (@in S<Int64>, @in S<Int64>) -> @out S<Int64> {
+sil hidden @test_devirt_of_partial_apply_of_witness_method : $@convention(thin) () -> @callee_owned (@in_guaranteed S<Int64>, @in_guaranteed S<Int64>) -> @out S<Int64> {
 bb0:
   %1 = metatype $@thick S<Int64>.Type
-  %2 = witness_method $S<Int64>, #CanAdd."+" : $@convention(witness_method: CanAdd) <τ_0_0 where τ_0_0 : CanAdd> (@in τ_0_0, @in τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0
-  %3 = partial_apply %2<S<Int64>>(%1) : $@convention(witness_method: CanAdd) <τ_0_0 where τ_0_0 : CanAdd> (@in τ_0_0, @in τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0
-  return %3 : $@callee_owned (@in S<Int64>, @in S<Int64>) -> @out S<Int64>
+  %2 = witness_method $S<Int64>, #CanAdd."+" : $@convention(witness_method: CanAdd) <τ_0_0 where τ_0_0 : CanAdd> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0
+  %3 = partial_apply %2<S<Int64>>(%1) : $@convention(witness_method: CanAdd) <τ_0_0 where τ_0_0 : CanAdd> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0
+  return %3 : $@callee_owned (@in_guaranteed S<Int64>, @in_guaranteed S<Int64>) -> @out S<Int64>
 }
 
 // Test that this application of witness_method can be devirtualized.
@@ -72,12 +73,12 @@ bb0:
 // CHECK: function_ref @operator_plus_static_non_generic_witness_for_S
 // CHECK: apply
 // CHECK: return
-sil hidden @test_devirt_of_apply_of_witness_method : $@convention(thin) (@in S<Int64>) -> S<Int64> {
+sil hidden @test_devirt_of_apply_of_witness_method : $@convention(thin) (@in_guaranteed S<Int64>) -> S<Int64> {
 bb0(%0 : $*S<Int64>):
   %1 = alloc_stack $S<Int64>
   %5 = metatype $@thick S<Int64>.Type
-  %6 = witness_method $S<Int64>, #CanAdd."+" : $@convention(witness_method: CanAdd) <τ_0_0 where τ_0_0 : CanAdd> (@in τ_0_0, @in τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0
-  %7 = apply %6<S<Int64>>(%1, %0, %0, %5) : $@convention(witness_method: CanAdd) <τ_0_0 where τ_0_0 : CanAdd> (@in τ_0_0, @in τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0
+  %6 = witness_method $S<Int64>, #CanAdd."+" : $@convention(witness_method: CanAdd) <τ_0_0 where τ_0_0 : CanAdd> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0
+  %7 = apply %6<S<Int64>>(%1, %0, %0, %5) : $@convention(witness_method: CanAdd) <τ_0_0 where τ_0_0 : CanAdd> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> @out τ_0_0
   %8 = load %1: $*S<Int64>
   dealloc_stack %1 : $*S<Int64>
   return %8 : $S<Int64>
diff --git a/test/SILOptimizer/function_uses.sil b/test/SILOptimizer/function_uses.sil
index 2885e7855cde9..b9027393e2a35 100644
--- a/test/SILOptimizer/function_uses.sil
+++ b/test/SILOptimizer/function_uses.sil
@@ -180,13 +180,13 @@ struct SP2 : P {
 // CHECK-NEXT:  <unknown uses>
 // CHECK-NEXT:  ]
 // CHECK-NEXT:  End function witness
-sil @witness : $@convention(witness_method: P) (@inout SP) -> () {
+sil @witness : $@convention(witness_method: P) (@in_guaranteed SP) -> () {
 bb0(%0 : $*SP):
   %7 = tuple ()
   return %7 : $()
 }
 
-sil @witness2 : $@convention(witness_method: P) (@inout SP) -> () {
+sil @witness2 : $@convention(witness_method: P) (@in_guaranteed SP) -> () {
 bb0(%0 : $*SP):
   %7 = tuple ()
   return %7 : $()
diff --git a/test/Serialization/Inputs/def_basic.sil b/test/Serialization/Inputs/def_basic.sil
index 35b2ca427caf3..3c364f6f5d422 100644
--- a/test/Serialization/Inputs/def_basic.sil
+++ b/test/Serialization/Inputs/def_basic.sil
@@ -1489,7 +1489,7 @@ bb0(%0 : $ConformingAssoc):
   return %2 : $()
 }
 
-sil [transparent] [serialized] @_TTWV14witness_tables15ConformingAssocS_9AssocReqtS_FS1_14requiredMethodU_fRQPS1_FT_T_ : $@convention(witness_method: AssocReqt) (@inout ConformingAssoc) -> () {
+sil [transparent] [serialized] @_TTWV14witness_tables15ConformingAssocS_9AssocReqtS_FS1_14requiredMethodU_fRQPS1_FT_T_ : $@convention(witness_method: AssocReqt) (@in_guaranteed ConformingAssoc) -> () {
 bb0(%0 : $*ConformingAssoc):
   %1 = load %0 : $*ConformingAssoc
   %2 = function_ref @$s14witness_tables15ConformingAssocV14requiredMethodyyycACF : $@convention(method) (@guaranteed ConformingAssoc) -> ()


Reviews:
user: login: rjmccall
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T17:40:11Z
user: login: DougGregor
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T07:00:44Z
user: login: gottesmm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T16:31:12Z
user: login: rjmccall
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T20:13:14Z

Review comments:
diff_hunk: @@ -2386,6 +2386,68 @@ static void destructureYieldsForCoroutine(TypeConverter &TC,
   }
 }
 
+std::optional<ActorIsolation>
+swift::getSILFunctionTypeActorIsolation(CanAnyFunctionType substFnInterfaceType,
+                                        std::optional<SILDeclRef> origConstant,
+                                        std::optional<SILDeclRef> constant) {
+  // If we have origConstant then we are creating a protocol method thunk. In
+  // such a case, we want to use the origConstant's actor isolation.
+  if (origConstant && constant &&
+      *origConstant != *constant) {
+    if (auto *decl = origConstant->getAbstractFunctionDecl()) {
+      if (auto *nonisolatedAttr =
+              decl->getAttrs().getAttribute<NonisolatedAttr>()) {
+        if (nonisolatedAttr->isNonSending())
+          return ActorIsolation::forCallerIsolationInheriting();
+      }
+
+      if (decl->getAttrs().hasAttribute<ConcurrentAttr>()) {
+        return ActorIsolation::forNonisolated(false /*unsafe*/);
+      }
+    }
+
+    return getActorIsolationOfContext(origConstant->getInnermostDeclContext());
path: lib/SIL/IR/SILFunctionType.cpp
user: login: rjmccall
body: If this is a generic global actor, does we need to do any kind of substitution to put it in the right generic signature?
created_at: 2025-05-19T17:40:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2386,6 +2386,68 @@ static void destructureYieldsForCoroutine(TypeConverter &TC,
   }
 }
 
+std::optional<ActorIsolation>
+swift::getSILFunctionTypeActorIsolation(CanAnyFunctionType substFnInterfaceType,
+                                        std::optional<SILDeclRef> origConstant,
+                                        std::optional<SILDeclRef> constant) {
+  // If we have origConstant then we are creating a protocol method thunk. In
+  // such a case, we want to use the origConstant's actor isolation.
+  if (origConstant && constant &&
+      *origConstant != *constant) {
+    if (auto *decl = origConstant->getAbstractFunctionDecl()) {
+      if (auto *nonisolatedAttr =
+              decl->getAttrs().getAttribute<NonisolatedAttr>()) {
+        if (nonisolatedAttr->isNonSending())
+          return ActorIsolation::forCallerIsolationInheriting();
+      }
+
+      if (decl->getAttrs().hasAttribute<ConcurrentAttr>()) {
+        return ActorIsolation::forNonisolated(false /*unsafe*/);
+      }
+    }
+
+    return getActorIsolationOfContext(origConstant->getInnermostDeclContext());
path: lib/SIL/IR/SILFunctionType.cpp
user: login: DougGregor
body: @gottesmm Do we need to do the generic substitution here?
created_at: 2025-05-22T07:00:43Z
author_association: MEMBER
diff_hunk: @@ -2386,6 +2386,68 @@ static void destructureYieldsForCoroutine(TypeConverter &TC,
   }
 }
 
+std::optional<ActorIsolation>
+swift::getSILFunctionTypeActorIsolation(CanAnyFunctionType substFnInterfaceType,
+                                        std::optional<SILDeclRef> origConstant,
+                                        std::optional<SILDeclRef> constant) {
+  // If we have origConstant then we are creating a protocol method thunk. In
+  // such a case, we want to use the origConstant's actor isolation.
+  if (origConstant && constant &&
+      *origConstant != *constant) {
+    if (auto *decl = origConstant->getAbstractFunctionDecl()) {
+      if (auto *nonisolatedAttr =
+              decl->getAttrs().getAttribute<NonisolatedAttr>()) {
+        if (nonisolatedAttr->isNonSending())
+          return ActorIsolation::forCallerIsolationInheriting();
+      }
+
+      if (decl->getAttrs().hasAttribute<ConcurrentAttr>()) {
+        return ActorIsolation::forNonisolated(false /*unsafe*/);
+      }
+    }
+
+    return getActorIsolationOfContext(origConstant->getInnermostDeclContext());
path: lib/SIL/IR/SILFunctionType.cpp
user: login: gottesmm
body: Can you elaborate on what is the concern here and give me a more complete explanation on what type of test case would generate the problem/why? I am missing a little bit of context.
created_at: 2025-05-22T16:31:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2386,6 +2386,68 @@ static void destructureYieldsForCoroutine(TypeConverter &TC,
   }
 }
 
+std::optional<ActorIsolation>
+swift::getSILFunctionTypeActorIsolation(CanAnyFunctionType substFnInterfaceType,
+                                        std::optional<SILDeclRef> origConstant,
+                                        std::optional<SILDeclRef> constant) {
+  // If we have origConstant then we are creating a protocol method thunk. In
+  // such a case, we want to use the origConstant's actor isolation.
+  if (origConstant && constant &&
+      *origConstant != *constant) {
+    if (auto *decl = origConstant->getAbstractFunctionDecl()) {
+      if (auto *nonisolatedAttr =
+              decl->getAttrs().getAttribute<NonisolatedAttr>()) {
+        if (nonisolatedAttr->isNonSending())
+          return ActorIsolation::forCallerIsolationInheriting();
+      }
+
+      if (decl->getAttrs().hasAttribute<ConcurrentAttr>()) {
+        return ActorIsolation::forNonisolated(false /*unsafe*/);
+      }
+    }
+
+    return getActorIsolationOfContext(origConstant->getInnermostDeclContext());
path: lib/SIL/IR/SILFunctionType.cpp
user: login: rjmccall
body: `ActorIsolation` can store a type if the function is isolated to a global actor. If the global actor is generic, then that type might includes references to type parameters. The concern is that this code might be implicitly moving the type between generic signatures without rewriting the type parameters — e.g. taking a type expressed in terms of the type parameters of the protocol requirement and using it in a context that's generic in the same way as the witness.
created_at: 2025-05-22T20:13:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: gottesmm
created_at: 2025-05-15T21:12:09Z
body: @swift-ci test
author_association: CONTRIBUTOR
user: login: gottesmm
created_at: 2025-05-15T21:12:14Z
body: @swift-ci test source compatibility
author_association: CONTRIBUTOR
user: login: gottesmm
created_at: 2025-05-15T21:14:00Z
body: I am doing a source compatibility check to see if the witness table ABI checking I added runs into anything else.
author_association: CONTRIBUTOR