[rbi] Fix demangling of sending results.

The issue here is that the demangler (since we have a postfix mangling) parses
parameters/results/etc and then uses earlier postfix type arguments to attach
the relevant types to the parameters/results/etc. Since the flag for a sending
result was placed in between the parameters and results, we get an off by one
error.

Rather than fix that specific issue by introducing an offset for the off by one
error, I used the fact that the impl-function part of the mangling is not ABI
and can be modified to move the bit used to signify a sending result to before
the parameters so the whole problem is avoided.

I also while I was doing this looked through the sending result mangling for any
further issues and fixed them as I found them.

rdar://141962865
This commit is contained in:
Michael Gottesman
2025-01-13 11:27:57 -08:00
parent bd3799885c
commit cb2d7560a4
9 changed files with 63 additions and 16 deletions

View File

@@ -837,7 +837,7 @@ mangled in to disambiguate.
impl-function-type ::= type* 'I' FUNC-ATTRIBUTES '_'
impl-function-type ::= type* generic-signature 'I' FUNC-ATTRIBUTES '_'
FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? ISOLATION? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? SENDABLE? ASYNC? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)?
FUNC-ATTRIBUTES ::= PATTERN-SUBS? INVOCATION-SUBS? PSEUDO-GENERIC? CALLEE-ESCAPE? ISOLATION? DIFFERENTIABILITY-KIND? CALLEE-CONVENTION FUNC-REPRESENTATION? COROUTINE-KIND? SENDABLE? ASYNC? SENDING-RESULT? (PARAM-CONVENTION PARAM-DIFFERENTIABILITY?)* RESULT-CONVENTION* ('Y' PARAM-CONVENTION)* ('z' RESULT-CONVENTION RESULT-DIFFERENTIABILITY?)?
PATTERN-SUBS ::= 's' // has pattern substitutions
INVOCATION-SUB ::= 'I' // has invocation substitutions
@@ -871,8 +871,9 @@ mangled in to disambiguate.
COROUTINE-KIND ::= 'G' // yield-many coroutine
#if SWIFT_RUNTIME_VERSION >= 5.5
SENDABLE ::= 'h' // @Sendable
ASYNC ::= 'H' // @async
SENDABLE ::= 'h' // @Sendable
ASYNC ::= 'H' // @async
SENDING-RESULT ::= 'T' // sending result
#endif
PARAM-CONVENTION ::= 'i' // indirect in

View File

@@ -1093,7 +1093,14 @@ protected:
flags = flags.withSendable();
} else if (child->getText() == "@async") {
flags = flags.withAsync();
} else if (child->getText() == "sending-result") {
flags = flags.withSendingResult();
}
} else if (child->getKind() == NodeKind::ImplSendingResult) {
// NOTE: This flag needs to be set both at the function level and on
// each of the parameters. The flag on the function just means that
// all parameters are sending (which is always true today).
flags = flags.withSendingResult();
} else if (child->getKind() == NodeKind::ImplCoroutineKind) {
if (!child->hasText())
return MAKE_NODE_TYPE_ERROR0(child, "expected text");

View File

@@ -677,6 +677,10 @@ Type ASTBuilder::createImplFunctionType(
auto type = result.getType()->getCanonicalType();
auto conv = getResultConvention(result.getConvention());
auto options = *getResultOptions(result.getOptions());
// We currently set sending result at the function level, but we set sending
// result on each result.
if (flags.hasSendingResult())
options |= SILResultInfo::IsSending;
funcResults.emplace_back(type, conv, options);
}

View File

@@ -2301,6 +2301,19 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn,
OpArgs.push_back('H');
}
// Mangle if we have a sending result and we are in a recursive position.
//
// DISCUSSION: We only want sending results to change ABI if it is using in a
// function value passed to a parameter or generic position... but not if it
// is just added to a return type.
//
// E.x.:
//
// func foo() -> sending X // No mangling
// func bar(_ x: () -> sending X) {} // Add to mangling for x
if (isInRecursion && fn->hasSendingResult())
OpArgs.push_back('T');
GenericSignature sig = fn->getSubstGenericSignature();
// Mangle the parameters.
@@ -2313,10 +2326,6 @@ void ASTMangler::appendImplFunctionType(SILFunctionType *fn,
appendType(param.getInterfaceType(), sig, forDecl);
}
// Mangle if we have a sending result.
if (isInRecursion && fn->hasSendingResult())
OpArgs.push_back('T');
// Mangle the results.
for (auto result : fn->getResults()) {
OpArgs.push_back(getResultConvention(result.getConvention()));

View File

@@ -2418,6 +2418,10 @@ NodePointer Demangler::demangleImplFunctionType() {
*this);
}
if (nextIf('T')) {
type->addChild(createNode(Node::Kind::ImplSendingResult), *this);
}
addChild(type, GenSig);
int NumTypesToAdd = 0;
@@ -2431,10 +2435,6 @@ NodePointer Demangler::demangleImplFunctionType() {
++NumTypesToAdd;
}
if (nextIf('T')) {
type->addChild(createNode(Node::Kind::ImplSendingResult), *this);
}
while (NodePointer Result = demangleImplResultConvention(
Node::Kind::ImplResult)) {
type = addChild(type, Result);
@@ -2467,7 +2467,7 @@ NodePointer Demangler::demangleImplFunctionType() {
return nullptr;
type->getChild(type->getNumChildren() - Idx - 1)->addChild(ConvTy, *this);
}
return createType(type);
}

View File

@@ -2252,6 +2252,9 @@ private:
if (Mangled.nextIf('H'))
addImplFunctionAttribute(type, "@async");
if (Mangled.nextIf('T'))
addImplFunctionAttribute(type, "sending-result");
// Enter a new generic context if this type is generic.
// FIXME: replace with std::optional, when we have it.
bool isPseudogeneric = false;

View File

@@ -2120,9 +2120,10 @@ ManglingError Remangler::mangleImplFunctionType(Node *node, unsigned depth) {
}
case Node::Kind::ImplFunctionAttribute: {
char FuncAttr = llvm::StringSwitch<char>(Child->getText())
.Case("@Sendable", 'h')
.Case("@async", 'H')
.Default(0);
.Case("@Sendable", 'h')
.Case("@async", 'H')
.Case("sending-result", 'T')
.Default(0);
if (!FuncAttr) {
return MANGLING_ERROR(ManglingError::InvalidImplFunctionAttribute,
Child);

View File

@@ -1,4 +1,6 @@
// RUN: %target-swift-frontend %s -emit-silgen -swift-version 6 | swift-demangle | %FileCheck -check-prefix=CHECK %s
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -emit-silgen -swift-version 6 | swift-demangle | %FileCheck %s
// RUN: %target-swift-frontend %s -emit-silgen -swift-version 6 | %FileCheck -check-prefix=SIL %s
// REQUIRES: concurrency
// REQUIRES: asserts
@@ -110,3 +112,21 @@ struct ConstructorSharedTest {
// CHECK: sil hidden [ossa] @sending_mangling.ConstructorSharedTest.functionSuppressed(sending_mangling.NonSendableKlass) -> () : $@convention(method) (@sil_sending @guaranteed NonSendableKlass, ConstructorSharedTest) -> () {
func functionSuppressed(_ x: __shared sending NonSendableKlass) {}
}
// Make sure that we produce the appropriate reabstraction thunk.
func reabstractionThunkTest_takeSendingReturnSending<T>(
_ x: sending T) -> sending T { fatalError() }
func reabstractionThunkTest_reabstractionThunkGenerator<T>(
_ x: sending T,
_ f: (sending T) -> T) {}
// CHECK: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @reabstraction thunk helper from @escaping @callee_guaranteed (@in sending sending_mangling.NonSendableKlass) -> sending (@out sending_mangling.NonSendableKlass) to @escaping @callee_guaranteed (@owned sending sending_mangling.NonSendableKlass) -> sending (@owned sending_mangling.NonSendableKlass) : $@convention(thin) (@sil_sending @owned NonSendableKlass, @guaranteed @callee_guaranteed (@sil_sending @in NonSendableKlass) -> @sil_sending @out NonSendableKlass) -> @sil_sending @owned NonSendableKlass {
// SIL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$s16sending_mangling16NonSendableKlassCACIegTiTr_A2CIegTxTo_TR : $@convention(thin) (@sil_sending @owned NonSendableKlass, @guaranteed @callee_guaranteed (@sil_sending @in NonSendableKlass) -> @sil_sending @out NonSendableKlass) -> @sil_sending @owned NonSendableKlass {
// CHECK: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @reabstraction thunk helper from @callee_guaranteed (@owned sending sending_mangling.NonSendableKlass) -> (@owned sending_mangling.NonSendableKlass) to @escaping @callee_guaranteed (@in sending sending_mangling.NonSendableKlass) -> (@out sending_mangling.NonSendableKlass) : $@convention(thin) (@sil_sending @in NonSendableKlass, @guaranteed @noescape @callee_guaranteed (@sil_sending @owned NonSendableKlass) -> @owned NonSendableKlass) -> @out NonSendableKlass {
// SIL: sil shared [transparent] [serialized] [reabstraction_thunk] [ossa] @$s16sending_mangling16NonSendableKlassCACIgxTo_A2CIegiTr_TR : $@convention(thin) (@sil_sending @in NonSendableKlass, @guaranteed @noescape @callee_guaranteed (@sil_sending @owned NonSendableKlass) -> @owned NonSendableKlass) -> @out NonSendableKlass {
func reabstractionThunkTest() {
reabstractionThunkTest_reabstractionThunkGenerator(
NonSendableKlass(),
reabstractionThunkTest_takeSendingReturnSending)
}

View File

@@ -485,3 +485,5 @@ $s4main4SlabVy$1_SiG ---> main.Slab<2, Swift.Int>
$s$n3_SSBV ---> Builtin.FixedArray<-4, Swift.String>
$s3red7MyActorC3runyxxyYaKACYcYTXEYaKlFZ ---> static red.MyActor.run<A>(@red.MyActor () async throws -> sending A) async throws -> A
$s3red7MyActorC3runyxxyYaKYAYTXEYaKlFZ ---> static red.MyActor.run<A>(@isolated(any) () async throws -> sending A) async throws -> A
$s7ToolKit10TypedValueOACs5Error_pIgHTnTrzo_A2CsAD_pIegHiTrzr_TR ---> {T:} reabstraction thunk helper from @callee_guaranteed @async (@in_guaranteed sending ToolKit.TypedValue) -> sending (@out ToolKit.TypedValue, @error @owned Swift.Error) to @escaping @callee_guaranteed @async (@in sending ToolKit.TypedValue) -> (@out ToolKit.TypedValue, @error @out Swift.Error)
$s16sending_mangling16NonSendableKlassCACIegTiTr_A2CIegTxTo_TR ---> {T:} reabstraction thunk helper from @escaping @callee_guaranteed (@in sending sending_mangling.NonSendableKlass) -> sending (@out sending_mangling.NonSendableKlass) to @escaping @callee_guaranteed (@owned sending sending_mangling.NonSendableKlass) -> sending (@owned sending_mangling.NonSendableKlass)