Skip to content

[6.2] Fix a few issues around nonisolated(nonsending) and protocol witness thunks/vtable thunks #81615

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: release/6.2
Choose a base branch
from
8 changes: 8 additions & 0 deletions include/swift/SIL/TypeLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
97 changes: 64 additions & 33 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2391,6 +2391,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.
///
Expand Down Expand Up @@ -2622,39 +2684,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,
Expand Down
104 changes: 79 additions & 25 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7658,57 +7658,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.
Expand Down
3 changes: 1 addition & 2 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading